library(swimplot) library(grid) library(gtable) library(readr)
library(mosaic) library(dplyr) library(survival) library(survminer)
library(ggplot2) library(scales) library(coxphf) library(ggthemes)
library(tidyverse) library(gtsummary) library(flextable)
library(parameters) library(car) library(ComplexHeatmap)
library(tidyverse) library(readxl) library(janitor) library(DT)
library(pROC) library(rms)
#ctDNA Detection Rates by Window and Stages
#ctDNA at MRD by Stage
rm(list=ls())
setwd("~/Downloads")
circ_data <- read.csv("RWE UTUC_Clinical Data 102025.csv")
circ_data <- circ_data[circ_data$Final.cohort=="TRUE",]
circ_data <- circ_data[circ_data$ctDNA.MRD!="",]
circ_data$ctDNA.MRD <- factor(circ_data$ctDNA.MRD, levels=c("NEGATIVE","POSITIVE"))
circ_data <- subset(circ_data, ctDNA.MRD %in% c("NEGATIVE", "POSITIVE"))
circ_data$Stage <- factor(circ_data$Stage, levels=c("0/I","II","III"))
positive_counts_by_stage <- aggregate(circ_data$ctDNA.MRD == "POSITIVE", by=list(circ_data$Stage), FUN=sum)
total_counts_by_stage <- aggregate(circ_data$ctDNA.MRD, by=list(circ_data$Stage), FUN=length)
combined_data <- data.frame(
Stage = total_counts_by_stage$Group.1,
Total_Count = total_counts_by_stage$x,
Positive_Count = positive_counts_by_stage$x,
Rate = (positive_counts_by_stage$x / total_counts_by_stage$x) * 100 # Convert to percentage
)
combined_data$Rate <- sprintf("%.2f%%", combined_data$Rate)
overall_total_count <- nrow(circ_data)
overall_positive_count <- nrow(circ_data[circ_data$ctDNA.MRD == "POSITIVE",])
overall_positivity_rate <- (overall_positive_count / overall_total_count) * 100 # Convert to percentage
overall_row <- data.frame(
Stage = "Overall",
Total_Count = overall_total_count,
Positive_Count = overall_positive_count,
Rate = sprintf("%.2f%%", overall_positivity_rate)
)
combined_data <- rbind(combined_data, overall_row)
print(combined_data)
Stage Total_Count Positive_Count Rate
1 0/I 6 2 33.33%
2 II 6 4 66.67%
3 III 16 9 56.25%
4 Overall 28 15 53.57%
#ctDNA at MRD by NAC status
rm(list=ls())
setwd("~/Downloads")
circ_data <- read.csv("RWE UTUC_Clinical Data 102025.csv")
circ_data <- circ_data[circ_data$Final.cohort=="TRUE",]
circ_data <- circ_data[circ_data$ctDNA.MRD!="",]
circ_data$ctDNA.MRD <- factor(circ_data$ctDNA.MRD, levels=c("NEGATIVE","POSITIVE"))
circ_data <- subset(circ_data, ctDNA.MRD %in% c("NEGATIVE", "POSITIVE"))
circ_data$NAC <- factor(circ_data$NAC, levels=c("FALSE","TRUE"))
positive_counts_by_stage <- aggregate(circ_data$ctDNA.MRD == "POSITIVE", by=list(circ_data$NAC), FUN=sum)
total_counts_by_stage <- aggregate(circ_data$ctDNA.MRD, by=list(circ_data$NAC), FUN=length)
combined_data <- data.frame(
Stage = total_counts_by_stage$Group.1,
Total_Count = total_counts_by_stage$x,
Positive_Count = positive_counts_by_stage$x,
Rate = (positive_counts_by_stage$x / total_counts_by_stage$x) * 100 # Convert to percentage
)
combined_data$Rate <- sprintf("%.2f%%", combined_data$Rate)
overall_total_count <- nrow(circ_data)
overall_positive_count <- nrow(circ_data[circ_data$ctDNA.MRD == "POSITIVE",])
overall_positivity_rate <- (overall_positive_count / overall_total_count) * 100 # Convert to percentage
overall_row <- data.frame(
Stage = "Overall",
Total_Count = overall_total_count,
Positive_Count = overall_positive_count,
Rate = sprintf("%.2f%%", overall_positivity_rate)
)
combined_data <- rbind(combined_data, overall_row)
print(combined_data)
Stage Total_Count Positive_Count Rate
1 FALSE 23 12 52.17%
2 TRUE 5 3 60.00%
3 Overall 28 15 53.57%
#ctDNA at Surveillance by Stage
rm(list=ls())
setwd("~/Downloads")
circ_data <- read.csv("RWE UTUC_Clinical Data 102025.csv")
circ_data <- circ_data[circ_data$Final.cohort=="TRUE",]
circ_data <- circ_data[circ_data$ctDNA.Surveillance!="",]
circ_data$ctDNA.Surveillance <- factor(circ_data$ctDNA.Surveillance, levels=c("NEGATIVE","POSITIVE"))
circ_data$Stage <- factor(circ_data$Stage, levels=c("0/I","II","III"))
positive_counts_by_stage <- aggregate(circ_data$ctDNA.Surveillance == "POSITIVE", by=list(circ_data$Stage), FUN=sum)
total_counts_by_stage <- aggregate(circ_data$ctDNA.Surveillance, by=list(circ_data$Stage), FUN=length)
combined_data <- data.frame(
Stage = total_counts_by_stage$Group.1,
Total_Count = total_counts_by_stage$x,
Positive_Count = positive_counts_by_stage$x,
Rate = (positive_counts_by_stage$x / total_counts_by_stage$x) * 100 # Convert to percentage
)
combined_data$Rate <- sprintf("%.2f%%", combined_data$Rate)
overall_total_count <- nrow(circ_data)
overall_positive_count <- nrow(circ_data[circ_data$ctDNA.Surveillance == "POSITIVE",])
overall_positivity_rate <- (overall_positive_count / overall_total_count) * 100 # Convert to percentage
overall_row <- data.frame(
Stage = "Overall",
Total_Count = overall_total_count,
Positive_Count = overall_positive_count,
Rate = sprintf("%.2f%%", overall_positivity_rate)
)
combined_data <- rbind(combined_data, overall_row)
print(combined_data)
Stage Total_Count Positive_Count Rate
1 0/I 9 3 33.33%
2 II 9 7 77.78%
3 III 22 12 54.55%
4 Overall 40 22 55.00%
#ctDNA at anytime by Stage
rm(list=ls())
setwd("~/Downloads")
circ_data <- read.csv("RWE UTUC_Clinical Data 102025.csv")
circ_data <- circ_data[circ_data$Final.cohort=="TRUE",]
circ_data <- circ_data[circ_data$ctDNA.anytime!="",]
circ_data$ctDNA.anytime <- factor(circ_data$ctDNA.anytime, levels=c("NEGATIVE","POSITIVE"))
circ_data$Stage <- factor(circ_data$Stage, levels=c("0/I","II","III"))
positive_counts_by_stage <- aggregate(circ_data$ctDNA.anytime == "POSITIVE", by=list(circ_data$Stage), FUN=sum)
total_counts_by_stage <- aggregate(circ_data$ctDNA.anytime, by=list(circ_data$Stage), FUN=length)
combined_data <- data.frame(
Stage = total_counts_by_stage$Group.1,
Total_Count = total_counts_by_stage$x,
Positive_Count = positive_counts_by_stage$x,
Rate = (positive_counts_by_stage$x / total_counts_by_stage$x) * 100 # Convert to percentage
)
combined_data$Rate <- sprintf("%.2f%%", combined_data$Rate)
overall_total_count <- nrow(circ_data)
overall_positive_count <- nrow(circ_data[circ_data$ctDNA.anytime == "POSITIVE",])
overall_positivity_rate <- (overall_positive_count / overall_total_count) * 100 # Convert to percentage
overall_row <- data.frame(
Stage = "Overall",
Total_Count = overall_total_count,
Positive_Count = overall_positive_count,
Rate = sprintf("%.2f%%", overall_positivity_rate)
)
combined_data <- rbind(combined_data, overall_row)
print(combined_data)
Stage Total_Count Positive_Count Rate
1 0/I 9 3 33.33%
2 II 10 8 80.00%
3 III 28 22 78.57%
4 Overall 47 33 70.21%
#Demographics Table
rm(list=ls())
setwd("~/Downloads")
circ_data <- read.csv("RWE UTUC_Clinical Data 102025.csv")
circ_data <- circ_data[circ_data$Final.cohort=="TRUE",]
circ_data_subset <- circ_data %>%
select(
Age,
Sex,
Stage,
NAC,
ACT,
ACT.type,
RFS.Event,
OS.Event,
OS.months) %>%
mutate(
Age = as.numeric(Age),
Sex = factor(Sex),
Stage = factor(Stage),
NAC = factor(NAC),
ACT = factor(ACT),
ACT.type = factor(ACT.type),
RFS.Event = factor(RFS.Event, levels = c("FALSE", "TRUE"), labels = c("No Recurrence", "Recurrence")),
OS.Event = factor(OS.Event, levels = c("FALSE", "TRUE"), labels = c("Alive", "Deceased")),
OS.months = as.numeric(OS.months))
table1 <- circ_data_subset %>%
tbl_summary(
statistic = list(
all_continuous() ~ "{median} ({min} - {max})",
all_categorical() ~ "{n} ({p}%)")) %>%
bold_labels()
table1
| Characteristic |
N = 47 |
| Age |
77 (47 - 92) |
| Sex |
|
| Female |
17 (36%) |
| Male |
30 (64%) |
| Stage |
|
| 0/I |
9 (19%) |
| II |
10 (21%) |
| III |
28 (60%) |
| NAC |
|
| FALSE |
40 (85%) |
| TRUE |
7 (15%) |
| ACT |
|
| FALSE |
13 (28%) |
| TRUE |
34 (72%) |
| ACT.type |
|
| |
13 (28%) |
| Chemotherapy |
18 (38%) |
| Immunotherapy |
16 (34%) |
| RFS.Event |
|
| No Recurrence |
23 (49%) |
| Recurrence |
24 (51%) |
| OS.Event |
|
| Alive |
41 (87%) |
| Deceased |
6 (13%) |
| OS.months |
21 (3 - 48) |
fit1 <- as_flex_table(
table1,
include = everything(),
return_calls = FALSE)
fit1
Characteristic | N = 471 |
|---|
Age | 77 (47 - 92) |
Sex |
|
Female | 17 (36%) |
Male | 30 (64%) |
Stage |
|
0/I | 9 (19%) |
II | 10 (21%) |
III | 28 (60%) |
NAC |
|
FALSE | 40 (85%) |
TRUE | 7 (15%) |
ACT |
|
FALSE | 13 (28%) |
TRUE | 34 (72%) |
ACT.type |
|
| 13 (28%) |
Chemotherapy | 18 (38%) |
Immunotherapy | 16 (34%) |
RFS.Event |
|
No Recurrence | 23 (49%) |
Recurrence | 24 (51%) |
OS.Event |
|
Alive | 41 (87%) |
Deceased | 6 (13%) |
OS.months | 21 (3 - 48) |
1Median (Min - Max); n (%) |
save_as_docx(fit1, path= "~/Downloads/1. Demographics Table_RWE UTUC.docx")
#Overview plot
rm(list=ls())
setwd("~/Downloads")
clinstage<- read.csv("RWE UTUC_OP 102025.csv")
clinstage <- clinstage[clinstage$Final.cohort=="TRUE",]
clinstage_df<- as.data.frame(clinstage)
#Display the swimmer plot with the label box
oplot<-swimmer_plot(df=clinstage_df,
id='PatientName',
end='fu.diff.months',
fill='gray',
width=.01,)
oplot <- oplot + theme(panel.border = element_blank())
oplot <- oplot + scale_y_continuous(breaks = seq(0, 96, by = 3))
oplot <- oplot + labs(x ="Patients" , y="Time from Surgery (Months)")
oplot

##plot events
oplot_ev1 <- oplot + swimmer_points(df_points=clinstage_df,
id='PatientName',
time='date.diff.months',
name_shape ='Event_type',
name_col = 'Event',
size=3.5,fill='black',
#col='darkgreen'
)
oplot_ev1

#Shape customization to Event_type
oplot_ev1.1 <- oplot_ev1 + ggplot2::scale_shape_manual(name="Event_type",values=c(1,16,6,18,4),breaks=c('ctDNA_neg','ctDNA_pos','Imaging','Surgery','Death'))
oplot_ev1.1

#plot treatment
oplot_ev2 <- oplot_ev1.1 + swimmer_lines(df_lines=clinstage_df,
id='PatientName',
start='Tx_start.months',
end='Tx_end.months',
name_col='Tx_type',
size=3.5,
name_alpha = 1.0)
oplot_ev2 <- oplot_ev2 + guides(linetype = guide_legend(override.aes = list(size = 5, color = "black")))
oplot_ev2

#colour customization
oplot_ev2.2 <- oplot_ev2 + ggplot2::scale_color_manual(name="Event",values=c( "lightblue","purple","green", "black", "black", "black", "lightblue","lightblue","green","red","green","green","blue","lightblue", "blue", "blue"))
oplot_ev2.2

#RFS by ctDNA status at MRD Window
rm(list=ls())
setwd("~/Downloads")
circ_data <- read.csv("RWE UTUC_Clinical Data 102025.csv")
circ_data <- circ_data[circ_data$Final.cohort=="TRUE",]
circ_data <- circ_data[circ_data$ctDNA.MRD!="",]
circ_data$RFS.MRD <- circ_data$RFS - circ_data$ctDNA.timing
circ_data$RFS.MRD.months <- circ_data$RFS.MRD / 30.437
survfit(Surv(time = circ_data$RFS.MRD.months, event = circ_data$RFS.Event)~ctDNA.MRD, data = circ_data)
Call: survfit(formula = Surv(time = circ_data$RFS.MRD.months, event = circ_data$RFS.Event) ~
ctDNA.MRD, data = circ_data)
n events median 0.95LCL 0.95UCL
ctDNA.MRD=NEGATIVE 13 3 NA 12.62 NA
ctDNA.MRD=POSITIVE 15 11 9.07 6.77 NA
event_summary <- circ_data %>%
group_by(ctDNA.MRD) %>%
summarise(
Total = n(),
Events = sum(RFS.Event),
Fraction = Events / n(),
Percentage = (Events / n()) * 100
)
print(event_summary)
# A tibble: 2 × 5
ctDNA.MRD Total Events Fraction Percentage
<chr> <int> <int> <dbl> <dbl>
1 NEGATIVE 13 3 0.231 23.1
2 POSITIVE 15 11 0.733 73.3
surv_object <-Surv(time = circ_data$RFS.MRD.months, event = circ_data$RFS.Event)
KM_curve <- survfit(surv_object ~ ctDNA.MRD, data = circ_data,conf.int=0.95,conf.type="log-log")
ggsurvplot(KM_curve, data = circ_data, pval = FALSE, conf.int = FALSE, risk.table = TRUE, break.time.by=3, palette=c("blue","red"), title="RFS - ctDNA at the MRD Window", ylab= "Recurrence-Free Survival", xlab="Time from Landmark Time point (months)", legend.labs=c("ctDNA Negative", "ctDNA Positive"), legend.title="")


summary(KM_curve, times= c(0, 12, 18))
Call: survfit(formula = surv_object ~ ctDNA.MRD, data = circ_data,
conf.int = 0.95, conf.type = "log-log")
ctDNA.MRD=NEGATIVE
time n.risk n.event survival std.err lower 95% CI upper 95% CI
0 13 0 1.000 0.000 1.000 1.000
12 8 2 0.818 0.116 0.447 0.951
18 5 1 0.716 0.140 0.350 0.899
ctDNA.MRD=POSITIVE
time n.risk n.event survival std.err lower 95% CI upper 95% CI
0 15 0 1.000 0.000 1.000 1.000
12 6 8 0.467 0.129 0.212 0.687
18 3 2 0.311 0.124 0.102 0.550
circ_data$ctDNA.MRD <- factor(circ_data$ctDNA.MRD, levels=c("NEGATIVE","POSITIVE"))
cox_fit <- coxph(surv_object ~ ctDNA.MRD, data=circ_data)
ggforest(cox_fit,data = circ_data)

summary(cox_fit)
Call:
coxph(formula = surv_object ~ ctDNA.MRD, data = circ_data)
n= 28, number of events= 14
coef exp(coef) se(coef) z Pr(>|z|)
ctDNA.MRDPOSITIVE 1.4752 4.3720 0.6539 2.256 0.0241 *
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
exp(coef) exp(-coef) lower .95 upper .95
ctDNA.MRDPOSITIVE 4.372 0.2287 1.214 15.75
Concordance= 0.677 (se = 0.057 )
Likelihood ratio test= 6.31 on 1 df, p=0.01
Wald test = 5.09 on 1 df, p=0.02
Score (logrank) test = 6.05 on 1 df, p=0.01
cox_fit_summary <- summary(cox_fit)
#Extract values for HR, 95% CI, and p-value
HR <- cox_fit_summary$coefficients[2]
lower_CI <- cox_fit_summary$conf.int[3]
upper_CI <- cox_fit_summary$conf.int[4]
p_value <- cox_fit_summary$coefficients[5]
label_text <- paste0("HR = ", round(HR, 2), " (", round(lower_CI, 2), "-", round(upper_CI, 2), "); p = ", round(p_value, 3))
print(label_text)
[1] "HR = 4.37 (1.21-15.75); p = 0.024"
circ_data$ctDNA.MRD <- factor(circ_data$ctDNA.MRD, levels = c("NEGATIVE", "POSITIVE"), labels = c("Negative", "Positive"))
circ_data$RFS.Event <- factor(circ_data$RFS.Event, levels = c("FALSE", "TRUE"), labels = c("No Recurrence", "Recurrence"))
contingency_table <- table(circ_data$ctDNA.MRD, circ_data$RFS.Event)
chi_square_test <- chisq.test(contingency_table)
print(chi_square_test)
Pearson's Chi-squared test with Yates' continuity correction
data: contingency_table
X-squared = 5.1692, df = 1, p-value = 0.02299
fisher_exact_test <- fisher.test(contingency_table)
print(fisher_exact_test)
Fisher's Exact Test for Count Data
data: contingency_table
p-value = 0.0213
alternative hypothesis: true odds ratio is not equal to 1
95 percent confidence interval:
1.28780 74.44434
sample estimates:
odds ratio
8.328277
print(contingency_table)
No Recurrence Recurrence
Negative 10 3
Positive 4 11
table_df <- as.data.frame(contingency_table)
table_df$Total <- ave(table_df$Freq, table_df$Var1, FUN = sum)
table_df$Percentage <- table_df$Freq / table_df$Total
table_df$MiddlePercentage <- table_df$Percentage / 2
ggplot(table_df, aes(x = Var1, y = Percentage, fill = Var2)) +
geom_bar(stat = "identity") +
geom_text(aes(y = MiddlePercentage, label = Freq), position = "stack", color = "black", vjust = 1.5, size = 7) +
theme_minimal() +
labs(title = "ctDNA status at MRD",
x = "ctDNA",
y = "Patients (%)",
fill = "Recurrence",
caption = paste("Fisher's exact test p-value: ", format.pval(fisher_exact_test$p.value))) +
scale_y_continuous(labels = scales::percent_format()) +
scale_fill_manual(values = c("No Recurrence" = "blue", "Recurrence" = "red")) + # define custom colors
theme(axis.text.x = element_text(angle = 0, hjust = 1.5, size = 14), # increase x-axis text size
axis.text.y = element_text(size = 14, color = "black"), # increase y-axis text size
axis.title.x = element_text(size = 14, color = "black"), # increase x-axis label size
axis.title.y = element_text(size = 14, color = "black"), # increase y-axis label size
legend.text = element_text(size = 12, color = "black")) # increase Recurrence label size

#RFS by ctDNA status at Surveillance Window
rm(list=ls())
setwd("~/Downloads")
circ_data <- read.csv("RWE UTUC_Clinical Data 102025.csv")
circ_data <- circ_data[circ_data$Final.cohort=="TRUE",]
circ_data <- circ_data[circ_data$ctDNA.Surveillance!="",]
survfit(Surv(time = circ_data$RFS.months, event = circ_data$RFS.Event)~ctDNA.Surveillance, data = circ_data)
Call: survfit(formula = Surv(time = circ_data$RFS.months, event = circ_data$RFS.Event) ~
ctDNA.Surveillance, data = circ_data)
n events median 0.95LCL 0.95UCL
ctDNA.Surveillance=NEGATIVE 18 3 NA NA NA
ctDNA.Surveillance=POSITIVE 22 17 13.9 10.1 NA
event_summary <- circ_data %>%
group_by(ctDNA.Surveillance) %>%
summarise(
Total = n(),
Events = sum(RFS.Event),
Fraction = Events / n(),
Percentage = (Events / n()) * 100
)
print(event_summary)
# A tibble: 2 × 5
ctDNA.Surveillance Total Events Fraction Percentage
<chr> <int> <int> <dbl> <dbl>
1 NEGATIVE 18 3 0.167 16.7
2 POSITIVE 22 17 0.773 77.3
surv_object <-Surv(time = circ_data$RFS.months, event = circ_data$RFS.Event)
KM_curve <- survfit(surv_object ~ ctDNA.Surveillance, data = circ_data,conf.int=0.95,conf.type="log-log")
ggsurvplot(KM_curve, data = circ_data, pval = FALSE, conf.int = FALSE, risk.table = TRUE, break.time.by=6, palette=c("blue","red"), title="RFS - ctDNA at the Surveillance Window", ylab= "Recurrence-Free Survival", xlab="Time from Surgery (months)", legend.labs=c("ctDNA Negative", "ctDNA Positive"), legend.title="")


summary(KM_curve, times= c(0, 12, 24))
Call: survfit(formula = surv_object ~ ctDNA.Surveillance, data = circ_data,
conf.int = 0.95, conf.type = "log-log")
ctDNA.Surveillance=NEGATIVE
time n.risk n.event survival std.err lower 95% CI upper 95% CI
0 18 0 1.000 0.0000 1.000 1.000
12 15 1 0.938 0.0605 0.632 0.991
24 5 2 0.757 0.1277 0.401 0.919
ctDNA.Surveillance=POSITIVE
time n.risk n.event survival std.err lower 95% CI upper 95% CI
0 22 0 1.000 0.000 1.000 1.000
12 12 9 0.591 0.105 0.361 0.762
24 4 6 0.287 0.101 0.114 0.488
circ_data$ctDNA.Surveillance <- factor(circ_data$ctDNA.Surveillance, levels=c("NEGATIVE","POSITIVE"))
cox_fit <- coxph(surv_object ~ ctDNA.Surveillance, data=circ_data)
ggforest(cox_fit,data = circ_data)

summary(cox_fit)
Call:
coxph(formula = surv_object ~ ctDNA.Surveillance, data = circ_data)
n= 40, number of events= 20
coef exp(coef) se(coef) z Pr(>|z|)
ctDNA.SurveillancePOSITIVE 1.8586 6.4151 0.6287 2.956 0.00311 **
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
exp(coef) exp(-coef) lower .95 upper .95
ctDNA.SurveillancePOSITIVE 6.415 0.1559 1.871 21.99
Concordance= 0.699 (se = 0.047 )
Likelihood ratio test= 12.46 on 1 df, p=4e-04
Wald test = 8.74 on 1 df, p=0.003
Score (logrank) test = 11.47 on 1 df, p=7e-04
cox_fit_summary <- summary(cox_fit)
#Extract values for HR, 95% CI, and p-value
HR <- cox_fit_summary$coefficients[2]
lower_CI <- cox_fit_summary$conf.int[3]
upper_CI <- cox_fit_summary$conf.int[4]
p_value <- cox_fit_summary$coefficients[5]
label_text <- paste0("HR = ", round(HR, 2), " (", round(lower_CI, 2), "-", round(upper_CI, 2), "); p = ", round(p_value, 3))
print(label_text)
[1] "HR = 6.42 (1.87-21.99); p = 0.003"
circ_data$ctDNA.Surveillance <- factor(circ_data$ctDNA.Surveillance, levels = c("NEGATIVE", "POSITIVE"), labels = c("Negative", "Positive"))
circ_data$RFS.Event <- factor(circ_data$RFS.Event, levels = c("FALSE", "TRUE"), labels = c("No Recurrence", "Recurrence"))
contingency_table <- table(circ_data$ctDNA.Surveillance, circ_data$RFS.Event)
chi_square_test <- chisq.test(contingency_table)
print(chi_square_test)
Pearson's Chi-squared test with Yates' continuity correction
data: contingency_table
X-squared = 12.222, df = 1, p-value = 0.0004722
fisher_exact_test <- fisher.test(contingency_table)
print(fisher_exact_test)
Fisher's Exact Test for Count Data
data: contingency_table
p-value = 0.0003284
alternative hypothesis: true odds ratio is not equal to 1
95 percent confidence interval:
2.867735 118.989345
sample estimates:
odds ratio
15.45796
print(contingency_table)
No Recurrence Recurrence
Negative 15 3
Positive 5 17
table_df <- as.data.frame(contingency_table)
table_df$Total <- ave(table_df$Freq, table_df$Var1, FUN = sum)
table_df$Percentage <- table_df$Freq / table_df$Total
table_df$MiddlePercentage <- table_df$Percentage / 2
ggplot(table_df, aes(x = Var1, y = Percentage, fill = Var2)) +
geom_bar(stat = "identity") +
geom_text(aes(y = MiddlePercentage, label = Freq), position = "stack", color = "black", vjust = 1.5, size = 7) +
theme_minimal() +
labs(title = "ctDNA status at Surveillance",
x = "ctDNA",
y = "Patients (%)",
fill = "Recurrence",
caption = paste("Fisher's exact test p-value: ", format.pval(fisher_exact_test$p.value))) +
scale_y_continuous(labels = scales::percent_format()) +
scale_fill_manual(values = c("No Recurrence" = "blue", "Recurrence" = "red")) + # define custom colors
theme(axis.text.x = element_text(angle = 0, hjust = 1.5, size = 14), # increase x-axis text size
axis.text.y = element_text(size = 14, color = "black"), # increase y-axis text size
axis.title.x = element_text(size = 14, color = "black"), # increase x-axis label size
axis.title.y = element_text(size = 14, color = "black"), # increase y-axis label size
legend.text = element_text(size = 12, color = "black")) # increase Recurrence label size

#Time-dependent analysis for RFS in longitudinal time points
rm(list=ls())
setwd("~/Downloads")
dt <- read_xlsx("CLIA UTUC Clinical Data_Time dependent.xlsx") |>
clean_names() |>
mutate(across(
.cols = c(window_start_date, dfs_date, surveillance_1_date:surveillance_12_date),
.fns = ~ as_date(as.Date(.x, format = "%Y-%m-%d"))
)) |>
filter(final_cohort == TRUE)
dt_biomarker <- dt |>
select(pts_id, ct_dna_surveillance_available,
window_start_date,
surveillance_1_status:surveillance_12_date) |>
filter(ct_dna_surveillance_available) |>
pivot_longer(cols = surveillance_1_status:surveillance_12_date,
names_to = c("visit_number", ".value"),
names_pattern = "surveillance_(.)_(.*)") |>
mutate(biomarker_time = day(days(date - window_start_date))) |>
select(pts_id, biomarker_time, biomarker_status = status) |>
filter(!is.na(biomarker_time))
glimpse(dt_biomarker)
Rows: 137
Columns: 3
$ pts_id <chr> "UTUC-002", "UTUC-010", "UTUC-010", "UTUC-010", "UTUC-010", "UTUC-010", "UTUC-010", "UTUC-010", "UTUC-010", "UTUC-010", "UTUC-011", "UTUC-011", "UTUC-…
$ biomarker_time <dbl> 133, 144, 181, 223, 267, 307, 349, 392, 433, 475, 74, 164, 204, 257, 385, 107, 145, 190, 267, 411, 78, 133, 267, 274, 341, 433, 42, 75, 21, 64, 111, 1…
$ biomarker_status <chr> "POSITIVE", "NEGATIVE", "NEGATIVE", "NEGATIVE", "NEGATIVE", "NEGATIVE", "NEGATIVE", "POSITIVE", "POSITIVE", "POSITIVE", "NEGATIVE", "POSITIVE", "POSIT…
dt_survival <- dt |>
select(pts_id, ct_dna_surveillance_available,
window_start_date:dfs_date, dfs_event) |> # Added dfs_event here
filter(ct_dna_surveillance_available) |>
mutate(dfs_time = (dfs_date - window_start_date),
dfs_time = day(days(dfs_time)),
dfs_event = as.numeric(dfs_event)) |>
select(pts_id, dfs_time, dfs_event)
glimpse(dt_survival)
Rows: 38
Columns: 3
$ pts_id <chr> "UTUC-002", "UTUC-010", "UTUC-011", "UTUC-012", "UTUC-015", "UTUC-026", "UTUC-027", "UTUC-028", "UTUC-030", "UTUC-031", "UTUC-032", "UTUC-034", "UTUC-035", "…
$ dfs_time <dbl> 143, 707, 651, 636, 671, 90, 260, 13, 424, 74, 614, 697, 120, 314, 594, 210, 627, 23, 72, 1080, 773, 482, 201, 347, 893, 426, 324, 662, 182, 557, 77, 219, 61…
$ dfs_event <dbl> 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0
aux <- dt_survival %>%
filter(dfs_time <= 0)
tab <- left_join(aux, dt) |>
select(pts_id, window_start_date, dfs_time, dfs_date,
surveillance_1_date:surveillance_12_date) |>
mutate(across(.cols = dfs_date:surveillance_12_date,
.fns = ~ as_date(.x))) |>
select(pts_id, window_start_date, dfs_date, dfs_time)
datatable(tab, filter = "top")
dt_survival <- dt_survival |>
filter(dfs_time > 0)
aux <- dt |>
select(pts_id, ct_dna_surveillance_available,
window_start_date, dfs_date,
surveillance_1_date:surveillance_12_date) |>
mutate(across(.cols = surveillance_1_date:surveillance_12_date,
.fns = ~ .x - window_start_date)) |>
mutate(across(.cols = surveillance_1_date:surveillance_12_date,
.fns = ~ .x < 0)) |>
rowwise() |>
mutate(sum_neg =
sum(c_across(surveillance_1_date:surveillance_12_date),
na.rm = TRUE)) |>
select(pts_id, sum_neg)
tab <- left_join(aux, dt) |>
filter(sum_neg > 0) |>
select(pts_id, sum_neg, window_start_date,
surveillance_1_date:surveillance_12_date) |>
mutate(across(.cols = window_start_date:surveillance_12_date,
.fns = ~ as_date(.x)))
datatable(tab, filter = "top")
aux <- dt |>
select(pts_id, ct_dna_surveillance_available,
window_start_date, dfs_date,
surveillance_1_date:surveillance_12_date) |>
mutate(across(.cols = dfs_date:surveillance_12_date,
.fns = ~ .x - window_start_date)) |>
mutate(across(.cols = surveillance_2_date:surveillance_12_date,
.fns = ~ dfs_date < .x)) |>
rowwise() |>
mutate(n_biomarker_after_event = sum(c_across(surveillance_2_date:
surveillance_12_date),
na.rm = TRUE)) |>
mutate(across(.cols = surveillance_1_date:surveillance_12_date,
.fns = ~ !is.na(.x))) |>
mutate(total_biomarker = sum(c_across(surveillance_2_date:
surveillance_12_date),
na.rm = TRUE)) |>
select(pts_id, n_biomarker_after_event, total_biomarker)
temp <- aux |>
select(-pts_id) |>
group_by(n_biomarker_after_event, total_biomarker) |> # Direct grouping
summarise(freq = n(), .groups = "drop") # Drop groups after summarization
tab <- left_join(aux, dt) |>
select(pts_id, n_biomarker_after_event, total_biomarker,
dfs_date,
surveillance_2_date:surveillance_12_date) |>
mutate(across(.cols = dfs_date:surveillance_12_date,
.fns = ~ as_date(.x))) |>
filter(n_biomarker_after_event > 0)
datatable(tab, filter = "top")
aux <- tmerge(data1 = dt_survival,
data2 = dt_survival,
id = pts_id,
dfs_event = event(dfs_time, dfs_event))
dt_final <- tmerge(data1 = aux,
data2 = dt_biomarker,
id = pts_id,
biomarker_status =
tdc(biomarker_time, biomarker_status))
datatable(dt_final, filter = "top")
# Syntax if there is not time-dependent covariate
# fit <- coxph(Surv(dfs_time, dfs_event) ~ biomarker_status,
# data = dt_final)
# summary(fit)
fit <- coxph(Surv(tstart, tstop, dfs_event) ~ biomarker_status,
data = dt_final)
summary(fit)
Call:
coxph(formula = Surv(tstart, tstop, dfs_event) ~ biomarker_status,
data = dt_final)
n= 127, number of events= 17
(36 observations deleted due to missingness)
coef exp(coef) se(coef) z Pr(>|z|)
biomarker_statusPOSITIVE 2.8209 16.7919 0.6524 4.324 1.53e-05 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
exp(coef) exp(-coef) lower .95 upper .95
biomarker_statusPOSITIVE 16.79 0.05955 4.675 60.32
Concordance= 0.824 (se = 0.055 )
Likelihood ratio test= 26.23 on 1 df, p=3e-07
Wald test = 18.7 on 1 df, p=2e-05
Score (logrank) test = 31 on 1 df, p=3e-08
cox_fit_summary <- summary(fit)
#Extract values for HR, 95% CI, and p-value
HR <- cox_fit_summary$coefficients[2]
lower_CI <- cox_fit_summary$conf.int[3]
upper_CI <- cox_fit_summary$conf.int[4]
p_value <- cox_fit_summary$coefficients[5]
label_text <- paste0("HR = ", round(HR, 2), " (", round(lower_CI, 2), "-", round(upper_CI, 2), "); p = ", round(p_value, 3))
print(label_text)
[1] "HR = 16.79 (4.67-60.32); p = 0"
#Median numbers of time points in the longitudinal setting
# Load the dataset
rm(list=ls())
setwd("~/Downloads")
circ_data <- read.csv("RWE UTUC_Clinical Data 102025.csv")
circ_data <- circ_data[circ_data$Final.cohort=="TRUE",]
circ_data <- circ_data[circ_data$ctDNA.Surveillance!="",]
circ_datadf <- as.data.frame(circ_data)
median_Nsurvtps <- median(circ_datadf$Nsurvtps, na.rm = TRUE)
min_Nsurvtps <- min(circ_datadf$Nsurvtps, na.rm = TRUE)
max_Nsurvtps <- max(circ_datadf$Nsurvtps, na.rm = TRUE)
cat(sprintf("Median # of surveillance time points: %d (%d-%d)\n",
median_Nsurvtps, min_Nsurvtps, max_Nsurvtps))
Median # of surveillance time points: 3 (1-12)
#Median of median interval of ctDNA timepoints and radiological
imaging assessment
rm(list=ls())
setwd("~/Downloads")
df <- read.csv("RWE UTUC_OP 102025.csv", stringsAsFactors = FALSE)
names(df) <- trimws(names(df))
# ---- Helper: compute median interval per patient ----
median_interval_per_patient <- function(data, filter_col, filter_val) {
data %>%
filter(!!sym(filter_col) == filter_val) %>%
arrange(PatientName, date.diff) %>%
group_by(PatientName) %>%
mutate(interval = date.diff - lag(date.diff)) %>%
summarise(median_interval = median(interval, na.rm = TRUE), .groups = "drop") %>%
mutate(event_group = filter_val)
}
# ---- Compute per-patient medians ----
ctDNA_patient_medians <- median_interval_per_patient(df, "Event", "ctDNA")
imaging_patient_medians <- median_interval_per_patient(df, "Event_type", "Imaging")
# ---- Function to summarize patient-level medians to cohort-level ----
cohort_summary <- function(patient_data, event_label) {
# Filter out patients with NA median intervals
valid_data <- patient_data %>% filter(!is.na(median_interval))
data.frame(
Event_Type = event_label,
Cohort_Median_Frequency = median(valid_data$median_interval, na.rm = TRUE),
Min_Patient_Median = min(valid_data$median_interval, na.rm = TRUE),
Max_Patient_Median = max(valid_data$median_interval, na.rm = TRUE),
Range_Patient_Median = max(valid_data$median_interval, na.rm = TRUE) -
min(valid_data$median_interval, na.rm = TRUE)
)
}
# ---- Combine all results ----
results <- bind_rows(
cohort_summary(ctDNA_patient_medians, "ctDNA"),
cohort_summary(imaging_patient_medians, "Imaging")
)
# ---- Print cohort-level summary ----
cat("===== Median Frequency (Days) per Cohort =====\n")
===== Median Frequency (Days) per Cohort =====
print(results, row.names = FALSE)
Event_Type Cohort_Median_Frequency Min_Patient_Median Max_Patient_Median Range_Patient_Median
ctDNA 75.75 14 156 142
Imaging 154.25 0 932 932
# ---- OPTIONAL: Save patient-level medians ----
patient_level_medians <- bind_rows(ctDNA_patient_medians, imaging_patient_medians)
#write.csv(patient_level_medians, "patient_median_intervals.csv", row.names = FALSE)
#Plot for individual lead-time calculations for each pt
rm(list=ls())
csv_path <- "~/Downloads/CLIA UTUC_Lead Time pts.csv"
df <- read.csv(csv_path, check.names = FALSE)
if ("Final.cohort" %in% names(df)) {
df <- df[df$Final.cohort == TRUE, ]
} else {
warning("Column 'Final.cohort' not found in the dataset.")
}
id_candidates <- c("Patient","ID","Pt","Subject","Sample")
id_col <- id_candidates[id_candidates %in% names(df)][1]
if (is.na(id_col)) {
df <- df %>% mutate(Patient = row_number())
id_col <- "Patient"
}
num_cols <- intersect(c("MR","CR","LT"), names(df))
df[num_cols] <- lapply(df[num_cols], function(x) suppressWarnings(as.numeric(x)))
# If LT missing, compute in days
if (!("LT" %in% names(df))) {
df <- df %>% mutate(LT = CR - MR)
}
# ---- convert to months ----
# Approximate: 30.44 days per month (average)
days_to_months <- function(x) x / 30.437
df <- df %>%
mutate(MR_mo = days_to_months(MR),
CR_mo = days_to_months(CR),
LT_mo = days_to_months(LT))
# ---- ordering for y-axis ----
df <- df %>%
mutate(Earliest = pmin(MR_mo, CR_mo, na.rm = TRUE)) %>%
arrange(Earliest) %>%
mutate(Patient_f = factor(.data[[id_col]], levels = .data[[id_col]]))
# ---- long format for points ----
points_long <- df %>%
select(Patient_f, MR_mo, CR_mo) %>%
pivot_longer(c(MR_mo, CR_mo), names_to = "Type", values_to = "Months") %>%
mutate(Type = dplyr::recode(Type,
"MR_mo" = "Molecular recurrence",
"CR_mo" = "Clinical recurrence"))
# ---- segments between MR and CR ----
segments_df <- df %>%
transmute(Patient_f,
x0 = MR_mo, x1 = CR_mo,
lt_flag = if_else(LT > 120, "gt120", "le120"))
# ---- annotation ----
med_lt <- median(df$LT_mo, na.rm = TRUE)
min_lt <- min(df$LT_mo, na.rm = TRUE)
max_lt <- max(df$LT_mo, na.rm = TRUE)
annot_label <- sprintf("Median lead-time:\n%.1f months (%.1f to %.1f)",
med_lt, min_lt, max_lt)
# ---- plot ----
pal <- c("Molecular recurrence" = "#10B4C1",
"Clinical recurrence" = "#C96A72")
x_max <- max(c(df$MR_mo, df$CR_mo), na.rm = TRUE)
y_mid <- levels(df$Patient_f)[ceiling(nlevels(df$Patient_f) * 0.55)]
p <- ggplot() +
geom_segment(data = segments_df,
aes(x = x0, xend = x1, y = Patient_f, yend = Patient_f,
linetype = lt_flag),
linewidth = 0.6, color = "black") +
scale_linetype_manual(values = c(le120 = "solid", gt120 = "dashed"),
guide = "none") +
geom_point(data = points_long,
aes(x = Months, y = Patient_f, color = Type),
size = 2.8) +
scale_color_manual(values = pal, name = NULL) +
labs(x = "Months from Surgery or Definitive Treatment", y = NULL) +
annotate("text",
x = x_max * 0.73,
y = y_mid,
label = annot_label,
hjust = 0, vjust = 0.5, size = 3.8) +
scale_x_continuous(breaks = seq(0, ceiling(x_max/3)*3, by = 3)) +
coord_cartesian(xlim = c(0, x_max * 1.05)) +
theme_bw(base_size = 12) +
theme(
panel.grid.major = element_line(color = "grey85", linewidth = 0.3),
panel.grid.minor = element_line(color = "grey92", linewidth = 0.2),
axis.text.y = element_text(size = 9)
)
print(p)

wc_df <- df %>% dplyr::select(MR, CR) %>% tidyr::drop_na()
paired_diff <- wc_df$CR - wc_df$MR
wilx <- wilcox.test(
x = wc_df$CR, y = wc_df$MR,
paired = TRUE,
alternative = "two.sided",
conf.int = TRUE, # gives CI for the Hodges–Lehmann estimate of the median difference
exact = FALSE # safer for ties/large N
)
W_stat <- unname(wilx$statistic) # Wilcoxon V
p_value <- wilx$p.value
HL_est <- unname(wilx$estimate) # median of (CR - MR)
HL_ci <- wilx$conf.int # CI for median difference
# Simple p-value formatter for annotation/publication
fmt_p <- function(p) {
if (is.na(p)) return("NA")
if (p < 0.001) "< 0.001" else sprintf("= %.3f", round(p, 3))
}
p_text <- paste0("P ", fmt_p(p_value)) # e.g., "P = 0.003" or "P < 0.001"
# Optional: print a compact summary
cat("\nPaired Wilcoxon signed-rank test (CR vs MR)\n",
"-------------------------------------------\n",
sprintf("N pairs: %d", nrow(wc_df)), "\n",
sprintf("W (V): %g", W_stat), "\n",
sprintf("P-value: %s", ifelse(p_value < 0.001, "< 0.001", sprintf("%.6f", p_value))), "\n",
sprintf("Hodges–Lehmann median difference (CR - MR): %.1f days", HL_est), "\n",
sprintf("95%% CI: [%.1f, %.1f] days", HL_ci[1], HL_ci[2]), "\n", sep = "")
Paired Wilcoxon signed-rank test (CR vs MR)
-------------------------------------------
N pairs: 21
W (V): 231
P-value: < 0.001
Hodges–Lehmann median difference (CR - MR): 155.5 days
95% CI: [86.0, 241.5] days
#Multivariate cox regression at Surveillance Window for RFS
rm(list=ls())
setwd("~/Downloads")
circ_data <- read.csv("RWE UTUC_Clinical Data 102025.csv")
circ_data <- circ_data[circ_data$Final.cohort=="TRUE",]
circ_data <- circ_data[circ_data$ctDNA.Surveillance!="",]
circ_data$ctDNA.Surveillance <- factor(circ_data$ctDNA.Surveillance, levels=c("NEGATIVE","POSITIVE"), labels = c("Negative", "Positive"))
circ_data$cStage <- factor(circ_data$cStage, levels = c("0/I", "II/III"))
circ_data$NAC <- factor(circ_data$NAC, levels = c("FALSE", "TRUE"))
circ_data$ACT <- factor(circ_data$ACT, levels = c("FALSE", "TRUE"))
surv_object <- Surv(time = circ_data$RFS.months, event = circ_data$RFS.Event)
cox_fit <- coxph(surv_object ~ ctDNA.Surveillance + Age + cStage + NAC + ACT, data=circ_data)
ggforest(cox_fit, data = circ_data, main = "Multivariate Regression Model for RFS - All Stages", refLabel = "Reference Group")


test.ph <- cox.zph(cox_fit)
#ctDNA and MTM/mL Dynamics for pts at surveillance window
#Dynamics and MTM/mL plots for patients with ctDNA negative at surveillance
rm(list=ls())
setwd("~/Downloads")
df <- read.csv("CLIA UTUC ctDNA MTM.csv", stringsAsFactors = FALSE)
df <- df[df$Final.cohort=="TRUE",]
df <- df[df$ctDNA.Surveillance=="NEGATIVE",]
df$RFS.Event <- ifelse(df$RFS.Event %in% c("No", "no", "FALSE", "False", "0"), FALSE,
ifelse(df$RFS.Event %in% c("Yes", "yes", "TRUE", "True", "1"), TRUE, NA))
df$RFS.Event <- factor(df$RFS.Event, levels = c(FALSE, TRUE))
df <- df %>%
group_by(PatientName) %>%
filter(n() >= 2) %>% #keep only pts with at least 2 post-surgery time points
ungroup()
num_unique <- length(unique(df$PatientName))
cat("Number of unique patients:", num_unique, "\n")
Number of unique patients: 17
df_patient_pfs <- df %>%
group_by(PatientName) %>%
dplyr::summarize(
PFS_True = any(RFS.Event == TRUE, na.rm = TRUE),
PFS_False = all(RFS.Event == FALSE, na.rm = TRUE)
)
num_true <- sum(df_patient_pfs$PFS_True)
num_false <- sum(df_patient_pfs$PFS_False)
cat("Number of unique patients with Event:", num_true, "\n")
Number of unique patients with Event: 3
cat("Number of unique patients with No Event:", num_false, "\n")
Number of unique patients with No Event: 14
p <- ggplot(df, aes(x = date.diff.months,
y = MTM.mL,
group = PatientName,
color = RFS.Event)) +
geom_line() + # Connect timepoints for each patient
geom_point() + # Add points for each timepoint
# Use a log10 scale for the y-axis with specified breaks
scale_y_log10(breaks = c(0.01, 0.1, 1, 10, 100, 1000),
labels = c("0.01","0.1", "1", "10", "100", "1000")) +
scale_x_continuous(breaks = seq(0, max(df$date.diff.months, na.rm = TRUE), by = 6)) +
scale_color_manual(values = c("FALSE" = "blue", "TRUE" = "red")) +
labs(
x = "Time Since Surgery or start of definitive treatment (months)",
y = "Mean Tumor Molecules per mL (MTM/mL)",
color = "RFS Event"
) +
theme_minimal()
print(p)

#Dynamics and MTM/mL plots for patients with ctDNA positive at surveillance
rm(list=ls())
setwd("~/Downloads")
df <- read.csv("CLIA UTUC ctDNA MTM.csv", stringsAsFactors = FALSE)
df <- df[df$Final.cohort=="TRUE",]
df <- df[df$ctDNA.Surveillance=="POSITIVE",]
df$RFS.Event <- ifelse(df$RFS.Event %in% c("No", "no", "FALSE", "False", "0"), FALSE,
ifelse(df$RFS.Event %in% c("Yes", "yes", "TRUE", "True", "1"), TRUE, NA))
df$RFS.Event <- factor(df$RFS.Event, levels = c(FALSE, TRUE))
df <- df %>%
group_by(PatientName) %>%
filter(n() >= 2) %>% #keep only pts with at least 2 post-surgery time points
ungroup()
num_unique <- length(unique(df$PatientName))
cat("Number of unique patients:", num_unique, "\n")
Number of unique patients: 21
df_patient_pfs <- df %>%
group_by(PatientName) %>%
dplyr::summarize(
PFS_True = any(RFS.Event == TRUE, na.rm = TRUE),
PFS_False = all(RFS.Event == FALSE, na.rm = TRUE)
)
num_true <- sum(df_patient_pfs$PFS_True)
num_false <- sum(df_patient_pfs$PFS_False)
cat("Number of unique patients with Event:", num_true, "\n")
Number of unique patients with Event: 16
cat("Number of unique patients with No Event:", num_false, "\n")
Number of unique patients with No Event: 5
p <- ggplot(df, aes(x = date.diff.months,
y = MTM.mL,
group = PatientName,
color = RFS.Event)) +
geom_line() + # Connect timepoints for each patient
geom_point() + # Add points for each timepoint
# Use a log10 scale for the y-axis with specified breaks
scale_y_log10(breaks = c(0.01, 0.1, 1, 10, 100, 1000),
labels = c("0.01","0.1", "1", "10", "100", "1000")) +
scale_x_continuous(breaks = seq(0, max(df$date.diff.months, na.rm = TRUE), by = 6)) +
scale_color_manual(values = c("FALSE" = "blue", "TRUE" = "red")) +
labs(
x = "Time Since Surgery or start of definitive treatment (months)",
y = "Mean Tumor Molecules per mL (MTM/mL)",
color = "RFS Event"
) +
theme_minimal()
print(p)

#ctDNA velocity and lead time liner regression
rm(list=ls())
csv_path <- "~/Downloads/CLIA UTUC_ctDNA velocity.csv" # <- adjust if needed
df_raw <- read.csv(csv_path, check.names = FALSE)
df <- df_raw %>%
rename(MTM_mL = `MTM.mL`) %>%
mutate(
daysCR.months = as.numeric(daysCR.months),
MTM_mL = as.numeric(MTM_mL)
) %>%
filter(Final.cohort == TRUE, !is.na(PatientName), !is.na(daysCR.months), !is.na(MTM_mL))
preCR <- df %>%
filter(daysCR.months <= 0, is.finite(MTM_mL), MTM_mL > 0)
eligible <- preCR %>%
group_by(PatientName) %>%
filter(n() >= 2, n_distinct(daysCR.months) >= 2) %>%
ungroup()
if (nrow(eligible) == 0) {
warning("No patients have ≥2 valid pre-recurrence points with distinct times; regression lines will be omitted.")
}
fits <- eligible %>%
group_by(PatientName) %>%
summarise(x_min = min(daysCR.months, na.rm = TRUE), .groups = "drop") %>%
mutate(grid = map(x_min, ~seq(.x, 0, length.out = 50))) %>%
select(PatientName, grid)
predict_patient <- function(dat, newx) {
if (length(newx) == 0) {
return(tibble(daysCR.months = numeric(0), MTM_mL = numeric(0)))
}
dat2 <- dat %>%
filter(is.finite(MTM_mL), MTM_mL > 0) %>%
mutate(log_ctdna = log10(MTM_mL))
if (nrow(dat2) < 2 || n_distinct(dat2$daysCR.months) < 2 || any(!is.finite(dat2$log_ctdna))) {
return(tibble(daysCR.months = numeric(0), MTM_mL = numeric(0)))
}
m <- lm(log_ctdna ~ daysCR.months, data = dat2)
tibble(
daysCR.months = newx,
MTM_mL = 10 ^ predict(m, newdata = tibble(daysCR.months = newx))
)
}
pred_lines <- eligible %>%
group_by(PatientName) %>%
tidyr::nest() %>% # list-column "data" per patient
left_join(fits, by = "PatientName") %>% # list-column "grid" per patient
mutate(pred = map2(data, grid, ~predict_patient(.x, .y))) %>%
select(PatientName, pred) %>%
tidyr::unnest(pred)
pooled_line <- {
if (nrow(preCR) >= 2 && n_distinct(preCR$daysCR.months) >= 2) {
pooled_x <- seq(min(preCR$daysCR.months, na.rm = TRUE), 0, length.out = 100)
pooled_fit <- lm(log10(MTM_mL) ~ daysCR.months, data = preCR)
tibble(
daysCR.months = pooled_x,
MTM_mL = 10 ^ predict(pooled_fit, newdata = tibble(daysCR.months = pooled_x))
)
} else {
tibble(daysCR.months = numeric(0), MTM_mL = numeric(0))
}
}
x_min <- floor(min(df$daysCR.months, na.rm = TRUE) / 3) * 3
x_max <- ceiling(max(df$daysCR.months, na.rm = TRUE) / 3) * 3
y_min_pos <- max(min(df$MTM_mL[df$MTM_mL > 0], na.rm = TRUE) / 2, 0.01)
y_max_pos <- 10 ^ ceiling(log10(max(df$MTM_mL, na.rm = TRUE)))
log_breaks <- 10 ^ seq(floor(log10(y_min_pos)), ceiling(log10(y_max_pos)))
p <- ggplot() +
# per-patient fitted lines (if any)
geom_line(data = pred_lines,
aes(x = daysCR.months, y = MTM_mL, color = PatientName),
linewidth = 1) +
# pooled dashed trend (if any)
geom_line(data = pooled_line,
aes(x = daysCR.months, y = MTM_mL),
linewidth = 0.8, linetype = "dashed", color = "grey35") +
# raw points (all timepoints, before and after)
geom_point(data = df,
aes(x = daysCR.months, y = MTM_mL, color = PatientName),
size = 1.8, alpha = 0.85) +
# vertical line at imaging-positive date
geom_vline(xintercept = 0, linetype = "dashed", linewidth = 0.8, color = "black") +
scale_y_log10(breaks = log_breaks,
labels = label_number(accuracy = 1)) +
scale_x_continuous(breaks = seq(x_min, x_max, by = 3)) + # every 3 months
labs(
x = "Months before/after clinical recurrence (0 = imaging positive)",
y = "ctDNA level (MTM/mL)",
color = "Patient"
) +
theme_bw(base_size = 12) +
theme(
panel.grid.major = element_line(color = "grey85", linewidth = 0.3),
panel.grid.minor = element_line(color = "grey92", linewidth = 0.2),
legend.position = "none" # change to "right" if you want the legend
)
print(p)

#Time from first ctDNA positive timepoint to last imaging in
surveillance_False Positive patients
rm(list=ls())
csv_path <- "~/Downloads/CLIA UTUC_Time from ctDNA to imaging FP pts.csv"
df <- read.csv(csv_path, check.names = FALSE)
if ("Final.cohort" %in% names(df)) {
df <- df[df$Final.cohort == TRUE, ]
} else {
warning("Column 'Final.cohort' not found in the dataset.")
}
id_candidates <- c("Patient","ID","Pt","Subject","Sample")
id_col <- id_candidates[id_candidates %in% names(df)][1]
if (is.na(id_col)) {
df <- df %>% mutate(Patient = row_number())
id_col <- "Patient"
}
num_cols <- intersect(c("MR","CR","LT"), names(df))
df[num_cols] <- lapply(df[num_cols], function(x) suppressWarnings(as.numeric(x)))
# If LT missing, compute in days
if (!("LT" %in% names(df))) {
df <- df %>% mutate(LT = CR - MR)
}
# ---- convert to months ----
# Approximate: 30.44 days per month (average)
days_to_months <- function(x) x / 30.437
df <- df %>%
mutate(MR_mo = days_to_months(MR),
CR_mo = days_to_months(CR),
LT_mo = days_to_months(LT))
# ---- ordering for y-axis ----
df <- df %>%
mutate(Earliest = pmin(MR_mo, CR_mo, na.rm = TRUE)) %>%
arrange(Earliest) %>%
mutate(Patient_f = factor(.data[[id_col]], levels = .data[[id_col]]))
# ---- long format for points ----
points_long <- df %>%
select(Patient_f, MR_mo, CR_mo) %>%
pivot_longer(c(MR_mo, CR_mo), names_to = "Type", values_to = "Months") %>%
mutate(Type = dplyr::recode(Type,
"MR_mo" = "First ctDNA positive",
"CR_mo" = "Last Radiological assessment"))
# ---- segments between MR and CR ----
segments_df <- df %>%
transmute(Patient_f,
x0 = MR_mo, x1 = CR_mo,
lt_flag = if_else(LT > 120, "gt120", "le120"))
# ---- annotation ----
med_lt <- median(df$LT_mo, na.rm = TRUE)
min_lt <- min(df$LT_mo, na.rm = TRUE)
max_lt <- max(df$LT_mo, na.rm = TRUE)
annot_label <- sprintf("Median time from ctDNA positive to last imaging:\n%.1f months (%.1f to %.1f)",
med_lt, min_lt, max_lt)
# ---- plot ----
pal <- c("First ctDNA positive" = "black",
"Last Radiological assessment" = "red")
x_max <- max(c(df$MR_mo, df$CR_mo), na.rm = TRUE)
y_mid <- levels(df$Patient_f)[ceiling(nlevels(df$Patient_f) * 0.55)]
p <- ggplot() +
geom_segment(data = segments_df,
aes(x = x0, xend = x1, y = Patient_f, yend = Patient_f,
linetype = lt_flag),
linewidth = 0.6, color = "black") +
scale_linetype_manual(values = c(le120 = "solid", gt120 = "dashed"),
guide = "none") +
geom_point(data = points_long,
aes(x = Months, y = Patient_f, color = Type),
size = 2.8) +
scale_color_manual(values = pal, name = NULL) +
labs(x = "Months from ctDNA positivity", y = NULL) +
annotate("text",
x = x_max * 0.73,
y = y_mid,
label = annot_label,
hjust = 0, vjust = 0.5, size = 3.8) +
scale_x_continuous(breaks = seq(0, ceiling(x_max/3)*3, by = 3)) +
coord_cartesian(xlim = c(0, x_max * 1.05)) +
theme_bw(base_size = 12) +
theme(
panel.grid.major = element_line(color = "grey85", linewidth = 0.3),
panel.grid.minor = element_line(color = "grey92", linewidth = 0.2),
axis.text.y = element_text(size = 9)
)
print(p)

wc_df <- df %>% dplyr::select(MR, CR) %>% tidyr::drop_na()
paired_diff <- wc_df$CR - wc_df$MR
wilx <- wilcox.test(
x = wc_df$CR, y = wc_df$MR,
paired = TRUE,
alternative = "two.sided",
conf.int = TRUE, # gives CI for the Hodges–Lehmann estimate of the median difference
exact = FALSE # safer for ties/large N
)
W_stat <- unname(wilx$statistic) # Wilcoxon V
p_value <- wilx$p.value
HL_est <- unname(wilx$estimate) # median of (CR - MR)
HL_ci <- wilx$conf.int # CI for median difference
# Simple p-value formatter for annotation/publication
fmt_p <- function(p) {
if (is.na(p)) return("NA")
if (p < 0.001) "< 0.001" else sprintf("= %.3f", round(p, 3))
}
p_text <- paste0("P ", fmt_p(p_value)) # e.g., "P = 0.003" or "P < 0.001"
# Optional: print a compact summary
cat("\nPaired Wilcoxon signed-rank test (CR vs MR)\n",
"-------------------------------------------\n",
sprintf("N pairs: %d", nrow(wc_df)), "\n",
sprintf("W (V): %g", W_stat), "\n",
sprintf("P-value: %s", ifelse(p_value < 0.001, "< 0.001", sprintf("%.6f", p_value))), "\n",
sprintf("Hodges–Lehmann median difference (CR - MR): %.1f days", HL_est), "\n",
sprintf("95%% CI: [%.1f, %.1f] days", HL_ci[1], HL_ci[2]), "\n", sep = "")
Paired Wilcoxon signed-rank test (CR vs MR)
-------------------------------------------
N pairs: 4
W (V): 10
P-value: 0.100348
Hodges–Lehmann median difference (CR - MR): 27.7 days
95% CI: [6.0, 291.0] days
#Individual Pt Plots_False Positive patients
#Filter for Patient UTUC-010
rm(list=ls())
setwd("~/Downloads")
circ_data <- read.csv("RWE UTUC_Pt Specific Plot data.csv")
plot_data <- circ_data %>%
filter(PatientName == "UTUC-010")
# 2. Process layers
# Handle ctDNA log-scale pseudo-zero
data_Signatera <- plot_data %>%
filter(Event_type %in% c("ctDNA_pos", "ctDNA_neg")) %>%
mutate(MTM_Log = ifelse(MTM.mL == 0, 0.001, MTM.mL))
data_Imaging <- plot_data %>%
filter(Event_type == "Imaging")
# Treatment blocks
data_Treatment <- plot_data %>%
filter(!is.na(Tx_type) & Tx_type != "NA" & !is.na(Tx_start.months)) %>%
distinct(Tx_type, Tx_start.months, Tx_end.months)
# 3. Create the Plot
ggplot() +
# Treatment Rectangles
geom_rect(data = data_Treatment,
aes(xmin = Tx_start.months, xmax = Tx_end.months, ymin = 0.004, ymax = 10),
fill = "lightblue", alpha = 0.15) +
geom_text(data = data_Treatment,
aes(x = (Tx_start.months + Tx_end.months)/2, y = 5, label = Tx_type),
angle = 90, color = "black", size = 3, fontface = "bold") +
# ctDNA Line
geom_line(data = data_Signatera,
aes(x = date.diff.months, y = MTM_Log), color = "black", linewidth = 0.7) +
# ctDNA Points (Mapped to Fill)
geom_point(data = data_Signatera,
aes(x = date.diff.months, y = MTM_Log, fill = Event_type),
shape = 21, size = 3.5, color = "black") +
# Imaging Points (Mapped to Shape and Color to create a second legend)
geom_point(data = data_Imaging,
aes(x = date.diff.months, y = 0.005, color = "NED in Scan"),
shape = 25, size = 4, fill = "darkgreen") +
# X-Axis: 3-month intervals
scale_x_continuous(breaks = seq(0, max(plot_data$date.diff.months, na.rm=T) + 3, by = 3)) +
# Y-Axis: Log10 scale
scale_y_log10(breaks = c(0.001, 0.01, 0.1, 1, 10),
labels = c("0", "0.01", "0.1", "1", "10"),
limits = c(0.001, 15)) +
# Legend for ctDNA
scale_fill_manual(values = c("ctDNA_pos" = "black", "ctDNA_neg" = "white"),
labels = c("Negative", "Positive"),
name = "ctDNA Status") +
# Legend for Imaging
scale_color_manual(values = c("NED in Scan" = "darkgreen"),
name = "Imaging Result") +
# Styling the legend to show the triangle correctly
guides(color = guide_legend(override.aes = list(shape = 25, fill = "darkgreen"))) +
labs(x = "Months from Surgery",
y = "MTM/mL",
title = "Clinical Timeline: UTUC-010") +
theme_minimal() +
theme(panel.grid.minor = element_blank(),
legend.position = "right")

#Filter for Patient UTUC-011
rm(list=ls())
setwd("~/Downloads")
circ_data <- read.csv("RWE UTUC_Pt Specific Plot data.csv")
plot_data <- circ_data %>%
filter(PatientName == "UTUC-011")
# 2. Process layers
# Handle ctDNA log-scale pseudo-zero
data_Signatera <- plot_data %>%
filter(Event_type %in% c("ctDNA_pos", "ctDNA_neg")) %>%
mutate(MTM_Log = ifelse(MTM.mL == 0, 0.001, MTM.mL))
data_Imaging <- plot_data %>%
filter(Event_type == "Imaging")
# Treatment blocks
data_Treatment <- plot_data %>%
filter(!is.na(Tx_type) & Tx_type != "NA" & !is.na(Tx_start.months)) %>%
distinct(Tx_type, Tx_start.months, Tx_end.months)
# 3. Create the Plot
ggplot() +
# Treatment Rectangles
geom_rect(data = data_Treatment,
aes(xmin = Tx_start.months, xmax = Tx_end.months, ymin = 0.004, ymax = 10),
fill = "lightblue", alpha = 0.15) +
geom_text(data = data_Treatment,
aes(x = (Tx_start.months + Tx_end.months)/2, y = 5, label = Tx_type),
angle = 90, color = "black", size = 3, fontface = "bold") +
# ctDNA Line
geom_line(data = data_Signatera,
aes(x = date.diff.months, y = MTM_Log), color = "black", linewidth = 0.7) +
# ctDNA Points (Mapped to Fill)
geom_point(data = data_Signatera,
aes(x = date.diff.months, y = MTM_Log, fill = Event_type),
shape = 21, size = 3.5, color = "black") +
# Imaging Points (Mapped to Shape and Color to create a second legend)
geom_point(data = data_Imaging,
aes(x = date.diff.months, y = 0.005, color = "NED in Scan"),
shape = 25, size = 4, fill = "darkgreen") +
# X-Axis: 3-month intervals
scale_x_continuous(breaks = seq(0, max(plot_data$date.diff.months, na.rm=T) + 3, by = 3)) +
# Y-Axis: Log10 scale
scale_y_log10(breaks = c(0.001, 0.01, 0.1, 1, 10),
labels = c("0", "0.01", "0.1", "1", "10"),
limits = c(0.001, 15)) +
# Legend for ctDNA
scale_fill_manual(values = c("ctDNA_pos" = "black", "ctDNA_neg" = "white"),
labels = c("Negative", "Positive"),
name = "ctDNA Status") +
# Legend for Imaging
scale_color_manual(values = c("NED in Scan" = "darkgreen"),
name = "Imaging Result") +
# Styling the legend to show the triangle correctly
guides(color = guide_legend(override.aes = list(shape = 25, fill = "darkgreen"))) +
labs(x = "Months from Surgery",
y = "MTM/mL",
title = "Clinical Timeline: UTUC-011") +
theme_minimal() +
theme(panel.grid.minor = element_blank(),
legend.position = "right")

LS0tCnRpdGxlOiAiQXlhbmFtYmFra2FtIGV0IGFsX2N0RE5BIGluIFVUVUMgQ2xpbmljYWwgQW5hbHlzaXMiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KbGlicmFyeShzd2ltcGxvdCkKbGlicmFyeShncmlkKQpsaWJyYXJ5KGd0YWJsZSkKbGlicmFyeShyZWFkcikgCmxpYnJhcnkobW9zYWljKQpsaWJyYXJ5KGRwbHlyKSAKbGlicmFyeShzdXJ2aXZhbCkgCmxpYnJhcnkoc3Vydm1pbmVyKSAKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KHNjYWxlcykKbGlicmFyeShjb3hwaGYpCmxpYnJhcnkoZ2d0aGVtZXMpCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGd0c3VtbWFyeSkKbGlicmFyeShmbGV4dGFibGUpCmxpYnJhcnkocGFyYW1ldGVycykKbGlicmFyeShjYXIpCmxpYnJhcnkoQ29tcGxleEhlYXRtYXApCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KHJlYWR4bCkKbGlicmFyeShqYW5pdG9yKQpsaWJyYXJ5KERUKQpsaWJyYXJ5KHBST0MpCmxpYnJhcnkocm1zKQoKI2N0RE5BIERldGVjdGlvbiBSYXRlcyBieSBXaW5kb3cgYW5kIFN0YWdlcwpgYGB7cn0KI2N0RE5BIGF0IE1SRCBieSBTdGFnZQpybShsaXN0PWxzKCkpCnNldHdkKCJ+L0Rvd25sb2FkcyIpCmNpcmNfZGF0YSA8LSByZWFkLmNzdigiUldFIFVUVUNfQ2xpbmljYWwgRGF0YSAxMDIwMjUuY3N2IikKY2lyY19kYXRhIDwtIGNpcmNfZGF0YVtjaXJjX2RhdGEkRmluYWwuY29ob3J0PT0iVFJVRSIsXQpjaXJjX2RhdGEgPC0gY2lyY19kYXRhW2NpcmNfZGF0YSRjdEROQS5NUkQhPSIiLF0KY2lyY19kYXRhJGN0RE5BLk1SRCA8LSBmYWN0b3IoY2lyY19kYXRhJGN0RE5BLk1SRCwgbGV2ZWxzPWMoIk5FR0FUSVZFIiwiUE9TSVRJVkUiKSkKY2lyY19kYXRhIDwtIHN1YnNldChjaXJjX2RhdGEsIGN0RE5BLk1SRCAlaW4lIGMoIk5FR0FUSVZFIiwgIlBPU0lUSVZFIikpCmNpcmNfZGF0YSRTdGFnZSA8LSBmYWN0b3IoY2lyY19kYXRhJFN0YWdlLCBsZXZlbHM9YygiMC9JIiwiSUkiLCJJSUkiKSkKcG9zaXRpdmVfY291bnRzX2J5X3N0YWdlIDwtIGFnZ3JlZ2F0ZShjaXJjX2RhdGEkY3RETkEuTVJEID09ICJQT1NJVElWRSIsIGJ5PWxpc3QoY2lyY19kYXRhJFN0YWdlKSwgRlVOPXN1bSkKdG90YWxfY291bnRzX2J5X3N0YWdlIDwtIGFnZ3JlZ2F0ZShjaXJjX2RhdGEkY3RETkEuTVJELCBieT1saXN0KGNpcmNfZGF0YSRTdGFnZSksIEZVTj1sZW5ndGgpCmNvbWJpbmVkX2RhdGEgPC0gZGF0YS5mcmFtZSgKICBTdGFnZSA9IHRvdGFsX2NvdW50c19ieV9zdGFnZSRHcm91cC4xLAogIFRvdGFsX0NvdW50ID0gdG90YWxfY291bnRzX2J5X3N0YWdlJHgsCiAgUG9zaXRpdmVfQ291bnQgPSBwb3NpdGl2ZV9jb3VudHNfYnlfc3RhZ2UkeCwKICBSYXRlID0gKHBvc2l0aXZlX2NvdW50c19ieV9zdGFnZSR4IC8gdG90YWxfY291bnRzX2J5X3N0YWdlJHgpICogMTAwICAjIENvbnZlcnQgdG8gcGVyY2VudGFnZQopCmNvbWJpbmVkX2RhdGEkUmF0ZSA8LSBzcHJpbnRmKCIlLjJmJSUiLCBjb21iaW5lZF9kYXRhJFJhdGUpCm92ZXJhbGxfdG90YWxfY291bnQgPC0gbnJvdyhjaXJjX2RhdGEpCm92ZXJhbGxfcG9zaXRpdmVfY291bnQgPC0gbnJvdyhjaXJjX2RhdGFbY2lyY19kYXRhJGN0RE5BLk1SRCA9PSAiUE9TSVRJVkUiLF0pCm92ZXJhbGxfcG9zaXRpdml0eV9yYXRlIDwtIChvdmVyYWxsX3Bvc2l0aXZlX2NvdW50IC8gb3ZlcmFsbF90b3RhbF9jb3VudCkgKiAxMDAgICMgQ29udmVydCB0byBwZXJjZW50YWdlCm92ZXJhbGxfcm93IDwtIGRhdGEuZnJhbWUoCiAgU3RhZ2UgPSAiT3ZlcmFsbCIsCiAgVG90YWxfQ291bnQgPSBvdmVyYWxsX3RvdGFsX2NvdW50LAogIFBvc2l0aXZlX0NvdW50ID0gb3ZlcmFsbF9wb3NpdGl2ZV9jb3VudCwKICBSYXRlID0gc3ByaW50ZigiJS4yZiUlIiwgb3ZlcmFsbF9wb3NpdGl2aXR5X3JhdGUpCikKY29tYmluZWRfZGF0YSA8LSByYmluZChjb21iaW5lZF9kYXRhLCBvdmVyYWxsX3JvdykKcHJpbnQoY29tYmluZWRfZGF0YSkKCiNjdEROQSBhdCBNUkQgYnkgTkFDIHN0YXR1cwpybShsaXN0PWxzKCkpCnNldHdkKCJ+L0Rvd25sb2FkcyIpCmNpcmNfZGF0YSA8LSByZWFkLmNzdigiUldFIFVUVUNfQ2xpbmljYWwgRGF0YSAxMDIwMjUuY3N2IikKY2lyY19kYXRhIDwtIGNpcmNfZGF0YVtjaXJjX2RhdGEkRmluYWwuY29ob3J0PT0iVFJVRSIsXQpjaXJjX2RhdGEgPC0gY2lyY19kYXRhW2NpcmNfZGF0YSRjdEROQS5NUkQhPSIiLF0KY2lyY19kYXRhJGN0RE5BLk1SRCA8LSBmYWN0b3IoY2lyY19kYXRhJGN0RE5BLk1SRCwgbGV2ZWxzPWMoIk5FR0FUSVZFIiwiUE9TSVRJVkUiKSkKY2lyY19kYXRhIDwtIHN1YnNldChjaXJjX2RhdGEsIGN0RE5BLk1SRCAlaW4lIGMoIk5FR0FUSVZFIiwgIlBPU0lUSVZFIikpCmNpcmNfZGF0YSROQUMgPC0gZmFjdG9yKGNpcmNfZGF0YSROQUMsIGxldmVscz1jKCJGQUxTRSIsIlRSVUUiKSkKcG9zaXRpdmVfY291bnRzX2J5X3N0YWdlIDwtIGFnZ3JlZ2F0ZShjaXJjX2RhdGEkY3RETkEuTVJEID09ICJQT1NJVElWRSIsIGJ5PWxpc3QoY2lyY19kYXRhJE5BQyksIEZVTj1zdW0pCnRvdGFsX2NvdW50c19ieV9zdGFnZSA8LSBhZ2dyZWdhdGUoY2lyY19kYXRhJGN0RE5BLk1SRCwgYnk9bGlzdChjaXJjX2RhdGEkTkFDKSwgRlVOPWxlbmd0aCkKY29tYmluZWRfZGF0YSA8LSBkYXRhLmZyYW1lKAogIFN0YWdlID0gdG90YWxfY291bnRzX2J5X3N0YWdlJEdyb3VwLjEsCiAgVG90YWxfQ291bnQgPSB0b3RhbF9jb3VudHNfYnlfc3RhZ2UkeCwKICBQb3NpdGl2ZV9Db3VudCA9IHBvc2l0aXZlX2NvdW50c19ieV9zdGFnZSR4LAogIFJhdGUgPSAocG9zaXRpdmVfY291bnRzX2J5X3N0YWdlJHggLyB0b3RhbF9jb3VudHNfYnlfc3RhZ2UkeCkgKiAxMDAgICMgQ29udmVydCB0byBwZXJjZW50YWdlCikKY29tYmluZWRfZGF0YSRSYXRlIDwtIHNwcmludGYoIiUuMmYlJSIsIGNvbWJpbmVkX2RhdGEkUmF0ZSkKb3ZlcmFsbF90b3RhbF9jb3VudCA8LSBucm93KGNpcmNfZGF0YSkKb3ZlcmFsbF9wb3NpdGl2ZV9jb3VudCA8LSBucm93KGNpcmNfZGF0YVtjaXJjX2RhdGEkY3RETkEuTVJEID09ICJQT1NJVElWRSIsXSkKb3ZlcmFsbF9wb3NpdGl2aXR5X3JhdGUgPC0gKG92ZXJhbGxfcG9zaXRpdmVfY291bnQgLyBvdmVyYWxsX3RvdGFsX2NvdW50KSAqIDEwMCAgIyBDb252ZXJ0IHRvIHBlcmNlbnRhZ2UKb3ZlcmFsbF9yb3cgPC0gZGF0YS5mcmFtZSgKICBTdGFnZSA9ICJPdmVyYWxsIiwKICBUb3RhbF9Db3VudCA9IG92ZXJhbGxfdG90YWxfY291bnQsCiAgUG9zaXRpdmVfQ291bnQgPSBvdmVyYWxsX3Bvc2l0aXZlX2NvdW50LAogIFJhdGUgPSBzcHJpbnRmKCIlLjJmJSUiLCBvdmVyYWxsX3Bvc2l0aXZpdHlfcmF0ZSkKKQpjb21iaW5lZF9kYXRhIDwtIHJiaW5kKGNvbWJpbmVkX2RhdGEsIG92ZXJhbGxfcm93KQpwcmludChjb21iaW5lZF9kYXRhKQoKI2N0RE5BIGF0IFN1cnZlaWxsYW5jZSBieSBTdGFnZQpybShsaXN0PWxzKCkpCnNldHdkKCJ+L0Rvd25sb2FkcyIpCmNpcmNfZGF0YSA8LSByZWFkLmNzdigiUldFIFVUVUNfQ2xpbmljYWwgRGF0YSAxMDIwMjUuY3N2IikKY2lyY19kYXRhIDwtIGNpcmNfZGF0YVtjaXJjX2RhdGEkRmluYWwuY29ob3J0PT0iVFJVRSIsXQpjaXJjX2RhdGEgPC0gY2lyY19kYXRhW2NpcmNfZGF0YSRjdEROQS5TdXJ2ZWlsbGFuY2UhPSIiLF0KY2lyY19kYXRhJGN0RE5BLlN1cnZlaWxsYW5jZSA8LSBmYWN0b3IoY2lyY19kYXRhJGN0RE5BLlN1cnZlaWxsYW5jZSwgbGV2ZWxzPWMoIk5FR0FUSVZFIiwiUE9TSVRJVkUiKSkKY2lyY19kYXRhJFN0YWdlIDwtIGZhY3RvcihjaXJjX2RhdGEkU3RhZ2UsIGxldmVscz1jKCIwL0kiLCJJSSIsIklJSSIpKQpwb3NpdGl2ZV9jb3VudHNfYnlfc3RhZ2UgPC0gYWdncmVnYXRlKGNpcmNfZGF0YSRjdEROQS5TdXJ2ZWlsbGFuY2UgPT0gIlBPU0lUSVZFIiwgYnk9bGlzdChjaXJjX2RhdGEkU3RhZ2UpLCBGVU49c3VtKQp0b3RhbF9jb3VudHNfYnlfc3RhZ2UgPC0gYWdncmVnYXRlKGNpcmNfZGF0YSRjdEROQS5TdXJ2ZWlsbGFuY2UsIGJ5PWxpc3QoY2lyY19kYXRhJFN0YWdlKSwgRlVOPWxlbmd0aCkKY29tYmluZWRfZGF0YSA8LSBkYXRhLmZyYW1lKAogIFN0YWdlID0gdG90YWxfY291bnRzX2J5X3N0YWdlJEdyb3VwLjEsCiAgVG90YWxfQ291bnQgPSB0b3RhbF9jb3VudHNfYnlfc3RhZ2UkeCwKICBQb3NpdGl2ZV9Db3VudCA9IHBvc2l0aXZlX2NvdW50c19ieV9zdGFnZSR4LAogIFJhdGUgPSAocG9zaXRpdmVfY291bnRzX2J5X3N0YWdlJHggLyB0b3RhbF9jb3VudHNfYnlfc3RhZ2UkeCkgKiAxMDAgICMgQ29udmVydCB0byBwZXJjZW50YWdlCikKY29tYmluZWRfZGF0YSRSYXRlIDwtIHNwcmludGYoIiUuMmYlJSIsIGNvbWJpbmVkX2RhdGEkUmF0ZSkKb3ZlcmFsbF90b3RhbF9jb3VudCA8LSBucm93KGNpcmNfZGF0YSkKb3ZlcmFsbF9wb3NpdGl2ZV9jb3VudCA8LSBucm93KGNpcmNfZGF0YVtjaXJjX2RhdGEkY3RETkEuU3VydmVpbGxhbmNlID09ICJQT1NJVElWRSIsXSkKb3ZlcmFsbF9wb3NpdGl2aXR5X3JhdGUgPC0gKG92ZXJhbGxfcG9zaXRpdmVfY291bnQgLyBvdmVyYWxsX3RvdGFsX2NvdW50KSAqIDEwMCAgIyBDb252ZXJ0IHRvIHBlcmNlbnRhZ2UKb3ZlcmFsbF9yb3cgPC0gZGF0YS5mcmFtZSgKICBTdGFnZSA9ICJPdmVyYWxsIiwKICBUb3RhbF9Db3VudCA9IG92ZXJhbGxfdG90YWxfY291bnQsCiAgUG9zaXRpdmVfQ291bnQgPSBvdmVyYWxsX3Bvc2l0aXZlX2NvdW50LAogIFJhdGUgPSBzcHJpbnRmKCIlLjJmJSUiLCBvdmVyYWxsX3Bvc2l0aXZpdHlfcmF0ZSkKKQpjb21iaW5lZF9kYXRhIDwtIHJiaW5kKGNvbWJpbmVkX2RhdGEsIG92ZXJhbGxfcm93KQpwcmludChjb21iaW5lZF9kYXRhKQoKI2N0RE5BIGF0IGFueXRpbWUgYnkgU3RhZ2UKcm0obGlzdD1scygpKQpzZXR3ZCgifi9Eb3dubG9hZHMiKQpjaXJjX2RhdGEgPC0gcmVhZC5jc3YoIlJXRSBVVFVDX0NsaW5pY2FsIERhdGEgMTAyMDI1LmNzdiIpCmNpcmNfZGF0YSA8LSBjaXJjX2RhdGFbY2lyY19kYXRhJEZpbmFsLmNvaG9ydD09IlRSVUUiLF0KY2lyY19kYXRhIDwtIGNpcmNfZGF0YVtjaXJjX2RhdGEkY3RETkEuYW55dGltZSE9IiIsXQpjaXJjX2RhdGEkY3RETkEuYW55dGltZSA8LSBmYWN0b3IoY2lyY19kYXRhJGN0RE5BLmFueXRpbWUsIGxldmVscz1jKCJORUdBVElWRSIsIlBPU0lUSVZFIikpCmNpcmNfZGF0YSRTdGFnZSA8LSBmYWN0b3IoY2lyY19kYXRhJFN0YWdlLCBsZXZlbHM9YygiMC9JIiwiSUkiLCJJSUkiKSkKcG9zaXRpdmVfY291bnRzX2J5X3N0YWdlIDwtIGFnZ3JlZ2F0ZShjaXJjX2RhdGEkY3RETkEuYW55dGltZSA9PSAiUE9TSVRJVkUiLCBieT1saXN0KGNpcmNfZGF0YSRTdGFnZSksIEZVTj1zdW0pCnRvdGFsX2NvdW50c19ieV9zdGFnZSA8LSBhZ2dyZWdhdGUoY2lyY19kYXRhJGN0RE5BLmFueXRpbWUsIGJ5PWxpc3QoY2lyY19kYXRhJFN0YWdlKSwgRlVOPWxlbmd0aCkKY29tYmluZWRfZGF0YSA8LSBkYXRhLmZyYW1lKAogIFN0YWdlID0gdG90YWxfY291bnRzX2J5X3N0YWdlJEdyb3VwLjEsCiAgVG90YWxfQ291bnQgPSB0b3RhbF9jb3VudHNfYnlfc3RhZ2UkeCwKICBQb3NpdGl2ZV9Db3VudCA9IHBvc2l0aXZlX2NvdW50c19ieV9zdGFnZSR4LAogIFJhdGUgPSAocG9zaXRpdmVfY291bnRzX2J5X3N0YWdlJHggLyB0b3RhbF9jb3VudHNfYnlfc3RhZ2UkeCkgKiAxMDAgICMgQ29udmVydCB0byBwZXJjZW50YWdlCikKY29tYmluZWRfZGF0YSRSYXRlIDwtIHNwcmludGYoIiUuMmYlJSIsIGNvbWJpbmVkX2RhdGEkUmF0ZSkKb3ZlcmFsbF90b3RhbF9jb3VudCA8LSBucm93KGNpcmNfZGF0YSkKb3ZlcmFsbF9wb3NpdGl2ZV9jb3VudCA8LSBucm93KGNpcmNfZGF0YVtjaXJjX2RhdGEkY3RETkEuYW55dGltZSA9PSAiUE9TSVRJVkUiLF0pCm92ZXJhbGxfcG9zaXRpdml0eV9yYXRlIDwtIChvdmVyYWxsX3Bvc2l0aXZlX2NvdW50IC8gb3ZlcmFsbF90b3RhbF9jb3VudCkgKiAxMDAgICMgQ29udmVydCB0byBwZXJjZW50YWdlCm92ZXJhbGxfcm93IDwtIGRhdGEuZnJhbWUoCiAgU3RhZ2UgPSAiT3ZlcmFsbCIsCiAgVG90YWxfQ291bnQgPSBvdmVyYWxsX3RvdGFsX2NvdW50LAogIFBvc2l0aXZlX0NvdW50ID0gb3ZlcmFsbF9wb3NpdGl2ZV9jb3VudCwKICBSYXRlID0gc3ByaW50ZigiJS4yZiUlIiwgb3ZlcmFsbF9wb3NpdGl2aXR5X3JhdGUpCikKY29tYmluZWRfZGF0YSA8LSByYmluZChjb21iaW5lZF9kYXRhLCBvdmVyYWxsX3JvdykKcHJpbnQoY29tYmluZWRfZGF0YSkKYGBgCgoKCiNEZW1vZ3JhcGhpY3MgVGFibGUKYGBge3J9CnJtKGxpc3Q9bHMoKSkKc2V0d2QoIn4vRG93bmxvYWRzIikgCmNpcmNfZGF0YSA8LSByZWFkLmNzdigiUldFIFVUVUNfQ2xpbmljYWwgRGF0YSAxMDIwMjUuY3N2IikKY2lyY19kYXRhIDwtIGNpcmNfZGF0YVtjaXJjX2RhdGEkRmluYWwuY29ob3J0PT0iVFJVRSIsXQoKY2lyY19kYXRhX3N1YnNldCA8LSBjaXJjX2RhdGEgJT4lCiAgc2VsZWN0KAogICAgQWdlLAogICAgU2V4LAogICAgU3RhZ2UsCiAgICBOQUMsCiAgICBBQ1QsCiAgICBBQ1QudHlwZSwKICAgIFJGUy5FdmVudCwKICAgIE9TLkV2ZW50LAogICAgT1MubW9udGhzKSAlPiUKICBtdXRhdGUoCiAgICBBZ2UgPSBhcy5udW1lcmljKEFnZSksCiAgICBTZXggPSBmYWN0b3IoU2V4KSwKICAgIFN0YWdlID0gZmFjdG9yKFN0YWdlKSwKICAgIE5BQyA9IGZhY3RvcihOQUMpLAogICAgQUNUID0gZmFjdG9yKEFDVCksCiAgICBBQ1QudHlwZSA9IGZhY3RvcihBQ1QudHlwZSksCiAgICBSRlMuRXZlbnQgPSBmYWN0b3IoUkZTLkV2ZW50LCBsZXZlbHMgPSBjKCJGQUxTRSIsICJUUlVFIiksIGxhYmVscyA9IGMoIk5vIFJlY3VycmVuY2UiLCAiUmVjdXJyZW5jZSIpKSwKICAgIE9TLkV2ZW50ID0gZmFjdG9yKE9TLkV2ZW50LCBsZXZlbHMgPSBjKCJGQUxTRSIsICJUUlVFIiksIGxhYmVscyA9IGMoIkFsaXZlIiwgIkRlY2Vhc2VkIikpLAogICAgT1MubW9udGhzID0gYXMubnVtZXJpYyhPUy5tb250aHMpKSAKdGFibGUxIDwtIGNpcmNfZGF0YV9zdWJzZXQgJT4lCiAgdGJsX3N1bW1hcnkoCiAgICBzdGF0aXN0aWMgPSBsaXN0KAogICAgICBhbGxfY29udGludW91cygpIH4gInttZWRpYW59ICh7bWlufSAtIHttYXh9KSIsCiAgICAgIGFsbF9jYXRlZ29yaWNhbCgpIH4gIntufSAoe3B9JSkiKSkgJT4lCiAgYm9sZF9sYWJlbHMoKQp0YWJsZTEKZml0MSA8LSBhc19mbGV4X3RhYmxlKAogIHRhYmxlMSwKICBpbmNsdWRlID0gZXZlcnl0aGluZygpLAogIHJldHVybl9jYWxscyA9IEZBTFNFKQpmaXQxCnNhdmVfYXNfZG9jeChmaXQxLCBwYXRoPSAifi9Eb3dubG9hZHMvMS4gRGVtb2dyYXBoaWNzIFRhYmxlX1JXRSBVVFVDLmRvY3giKQpgYGAKCgoKI092ZXJ2aWV3IHBsb3QKYGBge3J9CnJtKGxpc3Q9bHMoKSkKc2V0d2QoIn4vRG93bmxvYWRzIikgCmNsaW5zdGFnZTwtIHJlYWQuY3N2KCJSV0UgVVRVQ19PUCAxMDIwMjUuY3N2IikKY2xpbnN0YWdlIDwtIGNsaW5zdGFnZVtjbGluc3RhZ2UkRmluYWwuY29ob3J0PT0iVFJVRSIsXQpjbGluc3RhZ2VfZGY8LSBhcy5kYXRhLmZyYW1lKGNsaW5zdGFnZSkKCiNEaXNwbGF5IHRoZSBzd2ltbWVyIHBsb3Qgd2l0aCB0aGUgbGFiZWwgYm94Cm9wbG90PC1zd2ltbWVyX3Bsb3QoZGY9Y2xpbnN0YWdlX2RmLAogICAgICAgICAgICAgICAgICAgIGlkPSdQYXRpZW50TmFtZScsCiAgICAgICAgICAgICAgICAgICAgZW5kPSdmdS5kaWZmLm1vbnRocycsCiAgICAgICAgICAgICAgICAgICAgZmlsbD0nZ3JheScsCiAgICAgICAgICAgICAgICAgICAgd2lkdGg9LjAxLCkKb3Bsb3QgPC0gb3Bsb3QgKyB0aGVtZShwYW5lbC5ib3JkZXIgPSBlbGVtZW50X2JsYW5rKCkpCm9wbG90IDwtIG9wbG90ICsgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLCA5NiwgYnkgPSAzKSkKb3Bsb3QgPC0gb3Bsb3QgKyBsYWJzKHggPSJQYXRpZW50cyIgLCB5PSJUaW1lIGZyb20gU3VyZ2VyeSAoTW9udGhzKSIpCm9wbG90CgoKIyNwbG90IGV2ZW50cwpvcGxvdF9ldjEgPC0gb3Bsb3QgKyBzd2ltbWVyX3BvaW50cyhkZl9wb2ludHM9Y2xpbnN0YWdlX2RmLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZD0nUGF0aWVudE5hbWUnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aW1lPSdkYXRlLmRpZmYubW9udGhzJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmFtZV9zaGFwZSA9J0V2ZW50X3R5cGUnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYW1lX2NvbCA9ICdFdmVudCcsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpemU9My41LGZpbGw9J2JsYWNrJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI2NvbD0nZGFya2dyZWVuJwopCm9wbG90X2V2MQoKI1NoYXBlIGN1c3RvbWl6YXRpb24gdG8gRXZlbnRfdHlwZQoKb3Bsb3RfZXYxLjEgPC0gb3Bsb3RfZXYxICsgZ2dwbG90Mjo6c2NhbGVfc2hhcGVfbWFudWFsKG5hbWU9IkV2ZW50X3R5cGUiLHZhbHVlcz1jKDEsMTYsNiwxOCw0KSxicmVha3M9YygnY3RETkFfbmVnJywnY3RETkFfcG9zJywnSW1hZ2luZycsJ1N1cmdlcnknLCdEZWF0aCcpKQpvcGxvdF9ldjEuMQoKI3Bsb3QgdHJlYXRtZW50CgpvcGxvdF9ldjIgPC0gb3Bsb3RfZXYxLjEgKyBzd2ltbWVyX2xpbmVzKGRmX2xpbmVzPWNsaW5zdGFnZV9kZiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZD0nUGF0aWVudE5hbWUnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0YXJ0PSdUeF9zdGFydC5tb250aHMnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVuZD0nVHhfZW5kLm1vbnRocycsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmFtZV9jb2w9J1R4X3R5cGUnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpemU9My41LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hbWVfYWxwaGEgPSAxLjApCm9wbG90X2V2MiA8LSBvcGxvdF9ldjIgKyBndWlkZXMobGluZXR5cGUgPSBndWlkZV9sZWdlbmQob3ZlcnJpZGUuYWVzID0gbGlzdChzaXplID0gNSwgY29sb3IgPSAiYmxhY2siKSkpCm9wbG90X2V2MiAgCgojY29sb3VyIGN1c3RvbWl6YXRpb24Kb3Bsb3RfZXYyLjIgPC0gb3Bsb3RfZXYyICsgZ2dwbG90Mjo6c2NhbGVfY29sb3JfbWFudWFsKG5hbWU9IkV2ZW50Iix2YWx1ZXM9YyggImxpZ2h0Ymx1ZSIsInB1cnBsZSIsImdyZWVuIiwgImJsYWNrIiwgImJsYWNrIiwgImJsYWNrIiwgImxpZ2h0Ymx1ZSIsImxpZ2h0Ymx1ZSIsImdyZWVuIiwicmVkIiwiZ3JlZW4iLCJncmVlbiIsImJsdWUiLCJsaWdodGJsdWUiLCAiYmx1ZSIsICJibHVlIikpCm9wbG90X2V2Mi4yCmBgYAoKI1JGUyBieSBjdEROQSBzdGF0dXMgYXQgTVJEIFdpbmRvdwpgYGB7cn0Kcm0obGlzdD1scygpKQpzZXR3ZCgifi9Eb3dubG9hZHMiKSAKY2lyY19kYXRhIDwtIHJlYWQuY3N2KCJSV0UgVVRVQ19DbGluaWNhbCBEYXRhIDEwMjAyNS5jc3YiKQpjaXJjX2RhdGEgPC0gY2lyY19kYXRhW2NpcmNfZGF0YSRGaW5hbC5jb2hvcnQ9PSJUUlVFIixdCmNpcmNfZGF0YSA8LSBjaXJjX2RhdGFbY2lyY19kYXRhJGN0RE5BLk1SRCE9IiIsXQpjaXJjX2RhdGEkUkZTLk1SRCA8LSBjaXJjX2RhdGEkUkZTIC0gY2lyY19kYXRhJGN0RE5BLnRpbWluZwpjaXJjX2RhdGEkUkZTLk1SRC5tb250aHMgPC0gY2lyY19kYXRhJFJGUy5NUkQgLyAzMC40MzcKCnN1cnZmaXQoU3Vydih0aW1lID0gY2lyY19kYXRhJFJGUy5NUkQubW9udGhzLCBldmVudCA9IGNpcmNfZGF0YSRSRlMuRXZlbnQpfmN0RE5BLk1SRCwgZGF0YSA9IGNpcmNfZGF0YSkKZXZlbnRfc3VtbWFyeSA8LSBjaXJjX2RhdGEgJT4lCiAgZ3JvdXBfYnkoY3RETkEuTVJEKSAlPiUKICBzdW1tYXJpc2UoCiAgICBUb3RhbCA9IG4oKSwKICAgIEV2ZW50cyA9IHN1bShSRlMuRXZlbnQpLAogICAgRnJhY3Rpb24gPSBFdmVudHMgLyBuKCksCiAgICBQZXJjZW50YWdlID0gKEV2ZW50cyAvIG4oKSkgKiAxMDAKICApCnByaW50KGV2ZW50X3N1bW1hcnkpCnN1cnZfb2JqZWN0IDwtU3Vydih0aW1lID0gY2lyY19kYXRhJFJGUy5NUkQubW9udGhzLCBldmVudCA9IGNpcmNfZGF0YSRSRlMuRXZlbnQpCktNX2N1cnZlIDwtIHN1cnZmaXQoc3Vydl9vYmplY3QgfiBjdEROQS5NUkQsIGRhdGEgPSBjaXJjX2RhdGEsY29uZi5pbnQ9MC45NSxjb25mLnR5cGU9ImxvZy1sb2ciKSAKZ2dzdXJ2cGxvdChLTV9jdXJ2ZSwgZGF0YSA9IGNpcmNfZGF0YSwgcHZhbCA9IEZBTFNFLCBjb25mLmludCA9IEZBTFNFLCByaXNrLnRhYmxlID0gVFJVRSwgYnJlYWsudGltZS5ieT0zLCBwYWxldHRlPWMoImJsdWUiLCJyZWQiKSwgdGl0bGU9IlJGUyAtIGN0RE5BIGF0IHRoZSBNUkQgV2luZG93IiwgeWxhYj0gIlJlY3VycmVuY2UtRnJlZSBTdXJ2aXZhbCIsIHhsYWI9IlRpbWUgZnJvbSBMYW5kbWFyayBUaW1lIHBvaW50IChtb250aHMpIiwgbGVnZW5kLmxhYnM9YygiY3RETkEgTmVnYXRpdmUiLCAiY3RETkEgUG9zaXRpdmUiKSwgbGVnZW5kLnRpdGxlPSIiKQpzdW1tYXJ5KEtNX2N1cnZlLCB0aW1lcz0gYygwLCAxMiwgMTgpKQpjaXJjX2RhdGEkY3RETkEuTVJEIDwtIGZhY3RvcihjaXJjX2RhdGEkY3RETkEuTVJELCBsZXZlbHM9YygiTkVHQVRJVkUiLCJQT1NJVElWRSIpKQpjb3hfZml0IDwtIGNveHBoKHN1cnZfb2JqZWN0IH4gY3RETkEuTVJELCBkYXRhPWNpcmNfZGF0YSkgCmdnZm9yZXN0KGNveF9maXQsZGF0YSA9IGNpcmNfZGF0YSkgCnN1bW1hcnkoY294X2ZpdCkKY294X2ZpdF9zdW1tYXJ5IDwtIHN1bW1hcnkoY294X2ZpdCkKCiNFeHRyYWN0IHZhbHVlcyBmb3IgSFIsIDk1JSBDSSwgYW5kIHAtdmFsdWUKSFIgPC0gY294X2ZpdF9zdW1tYXJ5JGNvZWZmaWNpZW50c1syXQpsb3dlcl9DSSA8LSBjb3hfZml0X3N1bW1hcnkkY29uZi5pbnRbM10KdXBwZXJfQ0kgPC0gY294X2ZpdF9zdW1tYXJ5JGNvbmYuaW50WzRdCnBfdmFsdWUgPC0gY294X2ZpdF9zdW1tYXJ5JGNvZWZmaWNpZW50c1s1XQpsYWJlbF90ZXh0IDwtIHBhc3RlMCgiSFIgPSAiLCByb3VuZChIUiwgMiksICIgKCIsIHJvdW5kKGxvd2VyX0NJLCAyKSwgIi0iLCByb3VuZCh1cHBlcl9DSSwgMiksICIpOyBwID0gIiwgcm91bmQocF92YWx1ZSwgMykpCnByaW50KGxhYmVsX3RleHQpCgpjaXJjX2RhdGEkY3RETkEuTVJEIDwtIGZhY3RvcihjaXJjX2RhdGEkY3RETkEuTVJELCBsZXZlbHMgPSBjKCJORUdBVElWRSIsICJQT1NJVElWRSIpLCBsYWJlbHMgPSBjKCJOZWdhdGl2ZSIsICJQb3NpdGl2ZSIpKQpjaXJjX2RhdGEkUkZTLkV2ZW50IDwtIGZhY3RvcihjaXJjX2RhdGEkUkZTLkV2ZW50LCBsZXZlbHMgPSBjKCJGQUxTRSIsICJUUlVFIiksIGxhYmVscyA9IGMoIk5vIFJlY3VycmVuY2UiLCAiUmVjdXJyZW5jZSIpKQpjb250aW5nZW5jeV90YWJsZSA8LSB0YWJsZShjaXJjX2RhdGEkY3RETkEuTVJELCBjaXJjX2RhdGEkUkZTLkV2ZW50KQpjaGlfc3F1YXJlX3Rlc3QgPC0gY2hpc3EudGVzdChjb250aW5nZW5jeV90YWJsZSkKcHJpbnQoY2hpX3NxdWFyZV90ZXN0KQpmaXNoZXJfZXhhY3RfdGVzdCA8LSBmaXNoZXIudGVzdChjb250aW5nZW5jeV90YWJsZSkKcHJpbnQoZmlzaGVyX2V4YWN0X3Rlc3QpCnByaW50KGNvbnRpbmdlbmN5X3RhYmxlKQp0YWJsZV9kZiA8LSBhcy5kYXRhLmZyYW1lKGNvbnRpbmdlbmN5X3RhYmxlKQp0YWJsZV9kZiRUb3RhbCA8LSBhdmUodGFibGVfZGYkRnJlcSwgdGFibGVfZGYkVmFyMSwgRlVOID0gc3VtKQp0YWJsZV9kZiRQZXJjZW50YWdlIDwtIHRhYmxlX2RmJEZyZXEgLyB0YWJsZV9kZiRUb3RhbAp0YWJsZV9kZiRNaWRkbGVQZXJjZW50YWdlIDwtIHRhYmxlX2RmJFBlcmNlbnRhZ2UgLyAyCmdncGxvdCh0YWJsZV9kZiwgYWVzKHggPSBWYXIxLCB5ID0gUGVyY2VudGFnZSwgZmlsbCA9IFZhcjIpKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpICsKICBnZW9tX3RleHQoYWVzKHkgPSBNaWRkbGVQZXJjZW50YWdlLCBsYWJlbCA9IEZyZXEpLCBwb3NpdGlvbiA9ICJzdGFjayIsIGNvbG9yID0gImJsYWNrIiwgdmp1c3QgPSAxLjUsIHNpemUgPSA3KSArCiAgdGhlbWVfbWluaW1hbCgpICsKICBsYWJzKHRpdGxlID0gImN0RE5BIHN0YXR1cyBhdCBNUkQiLCAKICAgICAgIHggPSAiY3RETkEiLCAKICAgICAgIHkgPSAiUGF0aWVudHMgKCUpIiwgCiAgICAgICBmaWxsID0gIlJlY3VycmVuY2UiLAogICAgICAgY2FwdGlvbiA9IHBhc3RlKCJGaXNoZXIncyBleGFjdCB0ZXN0IHAtdmFsdWU6ICIsIGZvcm1hdC5wdmFsKGZpc2hlcl9leGFjdF90ZXN0JHAudmFsdWUpKSkgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBzY2FsZXM6OnBlcmNlbnRfZm9ybWF0KCkpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJObyBSZWN1cnJlbmNlIiA9ICJibHVlIiwgIlJlY3VycmVuY2UiID0gInJlZCIpKSArICMgZGVmaW5lIGN1c3RvbSBjb2xvcnMKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDAsIGhqdXN0ID0gMS41LCBzaXplID0gMTQpLCAjIGluY3JlYXNlIHgtYXhpcyB0ZXh0IHNpemUKICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIGNvbG9yID0gImJsYWNrIiksICMgaW5jcmVhc2UgeS1heGlzIHRleHQgc2l6ZQogICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIGNvbG9yID0gImJsYWNrIiksICMgaW5jcmVhc2UgeC1heGlzIGxhYmVsIHNpemUKICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0LCBjb2xvciA9ICJibGFjayIpLCAjIGluY3JlYXNlIHktYXhpcyBsYWJlbCBzaXplCiAgICAgICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyLCBjb2xvciA9ICJibGFjayIpKSAgIyBpbmNyZWFzZSBSZWN1cnJlbmNlIGxhYmVsIHNpemUKYGBgCgojUkZTIGJ5IGN0RE5BIHN0YXR1cyBhdCBTdXJ2ZWlsbGFuY2UgV2luZG93CmBgYHtyfQpybShsaXN0PWxzKCkpCnNldHdkKCJ+L0Rvd25sb2FkcyIpIApjaXJjX2RhdGEgPC0gcmVhZC5jc3YoIlJXRSBVVFVDX0NsaW5pY2FsIERhdGEgMTAyMDI1LmNzdiIpCmNpcmNfZGF0YSA8LSBjaXJjX2RhdGFbY2lyY19kYXRhJEZpbmFsLmNvaG9ydD09IlRSVUUiLF0KY2lyY19kYXRhIDwtIGNpcmNfZGF0YVtjaXJjX2RhdGEkY3RETkEuU3VydmVpbGxhbmNlIT0iIixdCgpzdXJ2Zml0KFN1cnYodGltZSA9IGNpcmNfZGF0YSRSRlMubW9udGhzLCBldmVudCA9IGNpcmNfZGF0YSRSRlMuRXZlbnQpfmN0RE5BLlN1cnZlaWxsYW5jZSwgZGF0YSA9IGNpcmNfZGF0YSkKZXZlbnRfc3VtbWFyeSA8LSBjaXJjX2RhdGEgJT4lCiAgZ3JvdXBfYnkoY3RETkEuU3VydmVpbGxhbmNlKSAlPiUKICBzdW1tYXJpc2UoCiAgICBUb3RhbCA9IG4oKSwKICAgIEV2ZW50cyA9IHN1bShSRlMuRXZlbnQpLAogICAgRnJhY3Rpb24gPSBFdmVudHMgLyBuKCksCiAgICBQZXJjZW50YWdlID0gKEV2ZW50cyAvIG4oKSkgKiAxMDAKICApCnByaW50KGV2ZW50X3N1bW1hcnkpCnN1cnZfb2JqZWN0IDwtU3Vydih0aW1lID0gY2lyY19kYXRhJFJGUy5tb250aHMsIGV2ZW50ID0gY2lyY19kYXRhJFJGUy5FdmVudCkKS01fY3VydmUgPC0gc3VydmZpdChzdXJ2X29iamVjdCB+IGN0RE5BLlN1cnZlaWxsYW5jZSwgZGF0YSA9IGNpcmNfZGF0YSxjb25mLmludD0wLjk1LGNvbmYudHlwZT0ibG9nLWxvZyIpIApnZ3N1cnZwbG90KEtNX2N1cnZlLCBkYXRhID0gY2lyY19kYXRhLCBwdmFsID0gRkFMU0UsIGNvbmYuaW50ID0gRkFMU0UsIHJpc2sudGFibGUgPSBUUlVFLCBicmVhay50aW1lLmJ5PTYsIHBhbGV0dGU9YygiYmx1ZSIsInJlZCIpLCB0aXRsZT0iUkZTIC0gY3RETkEgYXQgdGhlIFN1cnZlaWxsYW5jZSBXaW5kb3ciLCB5bGFiPSAiUmVjdXJyZW5jZS1GcmVlIFN1cnZpdmFsIiwgeGxhYj0iVGltZSBmcm9tIFN1cmdlcnkgKG1vbnRocykiLCBsZWdlbmQubGFicz1jKCJjdEROQSBOZWdhdGl2ZSIsICJjdEROQSBQb3NpdGl2ZSIpLCBsZWdlbmQudGl0bGU9IiIpCnN1bW1hcnkoS01fY3VydmUsIHRpbWVzPSBjKDAsIDEyLCAyNCkpCmNpcmNfZGF0YSRjdEROQS5TdXJ2ZWlsbGFuY2UgPC0gZmFjdG9yKGNpcmNfZGF0YSRjdEROQS5TdXJ2ZWlsbGFuY2UsIGxldmVscz1jKCJORUdBVElWRSIsIlBPU0lUSVZFIikpCmNveF9maXQgPC0gY294cGgoc3Vydl9vYmplY3QgfiBjdEROQS5TdXJ2ZWlsbGFuY2UsIGRhdGE9Y2lyY19kYXRhKSAKZ2dmb3Jlc3QoY294X2ZpdCxkYXRhID0gY2lyY19kYXRhKSAKc3VtbWFyeShjb3hfZml0KQpjb3hfZml0X3N1bW1hcnkgPC0gc3VtbWFyeShjb3hfZml0KQoKI0V4dHJhY3QgdmFsdWVzIGZvciBIUiwgOTUlIENJLCBhbmQgcC12YWx1ZQpIUiA8LSBjb3hfZml0X3N1bW1hcnkkY29lZmZpY2llbnRzWzJdCmxvd2VyX0NJIDwtIGNveF9maXRfc3VtbWFyeSRjb25mLmludFszXQp1cHBlcl9DSSA8LSBjb3hfZml0X3N1bW1hcnkkY29uZi5pbnRbNF0KcF92YWx1ZSA8LSBjb3hfZml0X3N1bW1hcnkkY29lZmZpY2llbnRzWzVdCmxhYmVsX3RleHQgPC0gcGFzdGUwKCJIUiA9ICIsIHJvdW5kKEhSLCAyKSwgIiAoIiwgcm91bmQobG93ZXJfQ0ksIDIpLCAiLSIsIHJvdW5kKHVwcGVyX0NJLCAyKSwgIik7IHAgPSAiLCByb3VuZChwX3ZhbHVlLCAzKSkKcHJpbnQobGFiZWxfdGV4dCkKCmNpcmNfZGF0YSRjdEROQS5TdXJ2ZWlsbGFuY2UgPC0gZmFjdG9yKGNpcmNfZGF0YSRjdEROQS5TdXJ2ZWlsbGFuY2UsIGxldmVscyA9IGMoIk5FR0FUSVZFIiwgIlBPU0lUSVZFIiksIGxhYmVscyA9IGMoIk5lZ2F0aXZlIiwgIlBvc2l0aXZlIikpCmNpcmNfZGF0YSRSRlMuRXZlbnQgPC0gZmFjdG9yKGNpcmNfZGF0YSRSRlMuRXZlbnQsIGxldmVscyA9IGMoIkZBTFNFIiwgIlRSVUUiKSwgbGFiZWxzID0gYygiTm8gUmVjdXJyZW5jZSIsICJSZWN1cnJlbmNlIikpCmNvbnRpbmdlbmN5X3RhYmxlIDwtIHRhYmxlKGNpcmNfZGF0YSRjdEROQS5TdXJ2ZWlsbGFuY2UsIGNpcmNfZGF0YSRSRlMuRXZlbnQpCmNoaV9zcXVhcmVfdGVzdCA8LSBjaGlzcS50ZXN0KGNvbnRpbmdlbmN5X3RhYmxlKQpwcmludChjaGlfc3F1YXJlX3Rlc3QpCmZpc2hlcl9leGFjdF90ZXN0IDwtIGZpc2hlci50ZXN0KGNvbnRpbmdlbmN5X3RhYmxlKQpwcmludChmaXNoZXJfZXhhY3RfdGVzdCkKcHJpbnQoY29udGluZ2VuY3lfdGFibGUpCnRhYmxlX2RmIDwtIGFzLmRhdGEuZnJhbWUoY29udGluZ2VuY3lfdGFibGUpCnRhYmxlX2RmJFRvdGFsIDwtIGF2ZSh0YWJsZV9kZiRGcmVxLCB0YWJsZV9kZiRWYXIxLCBGVU4gPSBzdW0pCnRhYmxlX2RmJFBlcmNlbnRhZ2UgPC0gdGFibGVfZGYkRnJlcSAvIHRhYmxlX2RmJFRvdGFsCnRhYmxlX2RmJE1pZGRsZVBlcmNlbnRhZ2UgPC0gdGFibGVfZGYkUGVyY2VudGFnZSAvIDIKZ2dwbG90KHRhYmxlX2RmLCBhZXMoeCA9IFZhcjEsIHkgPSBQZXJjZW50YWdlLCBmaWxsID0gVmFyMikpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikgKwogIGdlb21fdGV4dChhZXMoeSA9IE1pZGRsZVBlcmNlbnRhZ2UsIGxhYmVsID0gRnJlcSksIHBvc2l0aW9uID0gInN0YWNrIiwgY29sb3IgPSAiYmxhY2siLCB2anVzdCA9IDEuNSwgc2l6ZSA9IDcpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIGxhYnModGl0bGUgPSAiY3RETkEgc3RhdHVzIGF0IFN1cnZlaWxsYW5jZSIsIAogICAgICAgeCA9ICJjdEROQSIsIAogICAgICAgeSA9ICJQYXRpZW50cyAoJSkiLCAKICAgICAgIGZpbGwgPSAiUmVjdXJyZW5jZSIsCiAgICAgICBjYXB0aW9uID0gcGFzdGUoIkZpc2hlcidzIGV4YWN0IHRlc3QgcC12YWx1ZTogIiwgZm9ybWF0LnB2YWwoZmlzaGVyX2V4YWN0X3Rlc3QkcC52YWx1ZSkpKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IHNjYWxlczo6cGVyY2VudF9mb3JtYXQoKSkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIk5vIFJlY3VycmVuY2UiID0gImJsdWUiLCAiUmVjdXJyZW5jZSIgPSAicmVkIikpICsgIyBkZWZpbmUgY3VzdG9tIGNvbG9ycwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gMCwgaGp1c3QgPSAxLjUsIHNpemUgPSAxNCksICMgaW5jcmVhc2UgeC1heGlzIHRleHQgc2l6ZQogICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCwgY29sb3IgPSAiYmxhY2siKSwgIyBpbmNyZWFzZSB5LWF4aXMgdGV4dCBzaXplCiAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCwgY29sb3IgPSAiYmxhY2siKSwgIyBpbmNyZWFzZSB4LWF4aXMgbGFiZWwgc2l6ZQogICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIGNvbG9yID0gImJsYWNrIiksICMgaW5jcmVhc2UgeS1heGlzIGxhYmVsIHNpemUKICAgICAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIsIGNvbG9yID0gImJsYWNrIikpICAjIGluY3JlYXNlIFJlY3VycmVuY2UgbGFiZWwgc2l6ZQpgYGAKCiNUaW1lLWRlcGVuZGVudCBhbmFseXNpcyBmb3IgUkZTIGluIGxvbmdpdHVkaW5hbCB0aW1lIHBvaW50cwpgYGB7cn0Kcm0obGlzdD1scygpKQpzZXR3ZCgifi9Eb3dubG9hZHMiKQpkdCA8LSByZWFkX3hsc3goIkNMSUEgVVRVQyBDbGluaWNhbCBEYXRhX1RpbWUgZGVwZW5kZW50Lnhsc3giKSB8PgogIGNsZWFuX25hbWVzKCkgfD4KICBtdXRhdGUoYWNyb3NzKAogICAgLmNvbHMgPSBjKHdpbmRvd19zdGFydF9kYXRlLCBkZnNfZGF0ZSwgc3VydmVpbGxhbmNlXzFfZGF0ZTpzdXJ2ZWlsbGFuY2VfMTJfZGF0ZSksCiAgICAuZm5zID0gfiBhc19kYXRlKGFzLkRhdGUoLngsIGZvcm1hdCA9ICIlWS0lbS0lZCIpKQogICkpIHw+CiAgZmlsdGVyKGZpbmFsX2NvaG9ydCA9PSBUUlVFKQoKZHRfYmlvbWFya2VyIDwtIGR0IHw+CiAgc2VsZWN0KHB0c19pZCwgY3RfZG5hX3N1cnZlaWxsYW5jZV9hdmFpbGFibGUsCiAgICAgICAgIHdpbmRvd19zdGFydF9kYXRlLAogICAgICAgICBzdXJ2ZWlsbGFuY2VfMV9zdGF0dXM6c3VydmVpbGxhbmNlXzEyX2RhdGUpIHw+CiAgZmlsdGVyKGN0X2RuYV9zdXJ2ZWlsbGFuY2VfYXZhaWxhYmxlKSB8PgogIHBpdm90X2xvbmdlcihjb2xzID0gc3VydmVpbGxhbmNlXzFfc3RhdHVzOnN1cnZlaWxsYW5jZV8xMl9kYXRlLAogICAgICAgICAgICAgICBuYW1lc190byA9IGMoInZpc2l0X251bWJlciIsICIudmFsdWUiKSwKICAgICAgICAgICAgICAgbmFtZXNfcGF0dGVybiA9ICJzdXJ2ZWlsbGFuY2VfKC4pXyguKikiKSB8PgogIG11dGF0ZShiaW9tYXJrZXJfdGltZSA9IGRheShkYXlzKGRhdGUgLSB3aW5kb3dfc3RhcnRfZGF0ZSkpKSB8PgogIHNlbGVjdChwdHNfaWQsIGJpb21hcmtlcl90aW1lLCBiaW9tYXJrZXJfc3RhdHVzID0gc3RhdHVzKSB8PgogIGZpbHRlcighaXMubmEoYmlvbWFya2VyX3RpbWUpKQoKZ2xpbXBzZShkdF9iaW9tYXJrZXIpCgpkdF9zdXJ2aXZhbCA8LSBkdCB8PgogIHNlbGVjdChwdHNfaWQsIGN0X2RuYV9zdXJ2ZWlsbGFuY2VfYXZhaWxhYmxlLAogICAgICAgICB3aW5kb3dfc3RhcnRfZGF0ZTpkZnNfZGF0ZSwgZGZzX2V2ZW50KSB8PiAgIyBBZGRlZCBkZnNfZXZlbnQgaGVyZQogIGZpbHRlcihjdF9kbmFfc3VydmVpbGxhbmNlX2F2YWlsYWJsZSkgfD4KICBtdXRhdGUoZGZzX3RpbWUgPSAoZGZzX2RhdGUgLSB3aW5kb3dfc3RhcnRfZGF0ZSksCiAgICAgICAgIGRmc190aW1lID0gZGF5KGRheXMoZGZzX3RpbWUpKSwKICAgICAgICAgZGZzX2V2ZW50ID0gYXMubnVtZXJpYyhkZnNfZXZlbnQpKSB8PgogIHNlbGVjdChwdHNfaWQsIGRmc190aW1lLCBkZnNfZXZlbnQpCgpnbGltcHNlKGR0X3N1cnZpdmFsKQoKYXV4IDwtIGR0X3N1cnZpdmFsICU+JSAKICBmaWx0ZXIoZGZzX3RpbWUgPD0gMCkKCnRhYiA8LSBsZWZ0X2pvaW4oYXV4LCBkdCkgfD4KICBzZWxlY3QocHRzX2lkLCB3aW5kb3dfc3RhcnRfZGF0ZSwgZGZzX3RpbWUsIGRmc19kYXRlLAogICAgICAgICBzdXJ2ZWlsbGFuY2VfMV9kYXRlOnN1cnZlaWxsYW5jZV8xMl9kYXRlKSB8PgogIG11dGF0ZShhY3Jvc3MoLmNvbHMgPSBkZnNfZGF0ZTpzdXJ2ZWlsbGFuY2VfMTJfZGF0ZSwgCiAgICAgICAgICAgICAgICAuZm5zID0gfiBhc19kYXRlKC54KSkpIHw+CiAgc2VsZWN0KHB0c19pZCwgd2luZG93X3N0YXJ0X2RhdGUsIGRmc19kYXRlLCBkZnNfdGltZSkKCmRhdGF0YWJsZSh0YWIsIGZpbHRlciA9ICJ0b3AiKQoKZHRfc3Vydml2YWwgPC0gZHRfc3Vydml2YWwgfD4KICBmaWx0ZXIoZGZzX3RpbWUgPiAwKQoKYXV4IDwtIGR0IHw+CiAgc2VsZWN0KHB0c19pZCwgY3RfZG5hX3N1cnZlaWxsYW5jZV9hdmFpbGFibGUsCiAgICAgICAgIHdpbmRvd19zdGFydF9kYXRlLCBkZnNfZGF0ZSwKICAgICAgICAgc3VydmVpbGxhbmNlXzFfZGF0ZTpzdXJ2ZWlsbGFuY2VfMTJfZGF0ZSkgfD4KICBtdXRhdGUoYWNyb3NzKC5jb2xzID0gc3VydmVpbGxhbmNlXzFfZGF0ZTpzdXJ2ZWlsbGFuY2VfMTJfZGF0ZSwgCiAgICAgICAgICAgICAgICAuZm5zID0gfiAueCAtIHdpbmRvd19zdGFydF9kYXRlKSkgfD4KICBtdXRhdGUoYWNyb3NzKC5jb2xzID0gc3VydmVpbGxhbmNlXzFfZGF0ZTpzdXJ2ZWlsbGFuY2VfMTJfZGF0ZSwgCiAgICAgICAgICAgICAgICAuZm5zID0gfiAueCA8IDApKSB8PgogIHJvd3dpc2UoKSB8PgogIG11dGF0ZShzdW1fbmVnID0gCiAgICAgICAgICAgc3VtKGNfYWNyb3NzKHN1cnZlaWxsYW5jZV8xX2RhdGU6c3VydmVpbGxhbmNlXzEyX2RhdGUpLAogICAgICAgICAgICAgICBuYS5ybSA9IFRSVUUpKSAgfD4KICBzZWxlY3QocHRzX2lkLCBzdW1fbmVnKQoKdGFiIDwtIGxlZnRfam9pbihhdXgsIGR0KSB8PgogIGZpbHRlcihzdW1fbmVnID4gMCkgfD4KICBzZWxlY3QocHRzX2lkLCBzdW1fbmVnLCB3aW5kb3dfc3RhcnRfZGF0ZSwKICAgICAgICAgc3VydmVpbGxhbmNlXzFfZGF0ZTpzdXJ2ZWlsbGFuY2VfMTJfZGF0ZSkgfD4KICBtdXRhdGUoYWNyb3NzKC5jb2xzID0gd2luZG93X3N0YXJ0X2RhdGU6c3VydmVpbGxhbmNlXzEyX2RhdGUsIAogICAgICAgICAgICAgICAgLmZucyA9IH4gYXNfZGF0ZSgueCkpKSAKCmRhdGF0YWJsZSh0YWIsIGZpbHRlciA9ICJ0b3AiKQoKYXV4IDwtIGR0IHw+CiAgc2VsZWN0KHB0c19pZCwgY3RfZG5hX3N1cnZlaWxsYW5jZV9hdmFpbGFibGUsCiAgICAgICAgIHdpbmRvd19zdGFydF9kYXRlLCBkZnNfZGF0ZSwKICAgICAgICAgc3VydmVpbGxhbmNlXzFfZGF0ZTpzdXJ2ZWlsbGFuY2VfMTJfZGF0ZSkgfD4KICBtdXRhdGUoYWNyb3NzKC5jb2xzID0gZGZzX2RhdGU6c3VydmVpbGxhbmNlXzEyX2RhdGUsIAogICAgICAgICAgICAgICAgLmZucyA9IH4gLnggLSB3aW5kb3dfc3RhcnRfZGF0ZSkpIHw+CiAgbXV0YXRlKGFjcm9zcyguY29scyA9IHN1cnZlaWxsYW5jZV8yX2RhdGU6c3VydmVpbGxhbmNlXzEyX2RhdGUsCiAgICAgICAgICAgICAgICAuZm5zID0gfiBkZnNfZGF0ZSA8IC54KSkgfD4KICByb3d3aXNlKCkgfD4KICBtdXRhdGUobl9iaW9tYXJrZXJfYWZ0ZXJfZXZlbnQgPSBzdW0oY19hY3Jvc3Moc3VydmVpbGxhbmNlXzJfZGF0ZToKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdXJ2ZWlsbGFuY2VfMTJfZGF0ZSksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYS5ybSA9IFRSVUUpKSB8PgogIG11dGF0ZShhY3Jvc3MoLmNvbHMgPSBzdXJ2ZWlsbGFuY2VfMV9kYXRlOnN1cnZlaWxsYW5jZV8xMl9kYXRlLAogICAgICAgICAgICAgICAgLmZucyA9IH4gIWlzLm5hKC54KSkpIHw+CiAgbXV0YXRlKHRvdGFsX2Jpb21hcmtlciA9IHN1bShjX2Fjcm9zcyhzdXJ2ZWlsbGFuY2VfMl9kYXRlOgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdXJ2ZWlsbGFuY2VfMTJfZGF0ZSksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmEucm0gPSBUUlVFKSkgfD4KICBzZWxlY3QocHRzX2lkLCBuX2Jpb21hcmtlcl9hZnRlcl9ldmVudCwgdG90YWxfYmlvbWFya2VyKQoKdGVtcCA8LSBhdXggfD4gCiAgc2VsZWN0KC1wdHNfaWQpIHw+IAogIGdyb3VwX2J5KG5fYmlvbWFya2VyX2FmdGVyX2V2ZW50LCB0b3RhbF9iaW9tYXJrZXIpIHw+ICAjIERpcmVjdCBncm91cGluZwogIHN1bW1hcmlzZShmcmVxID0gbigpLCAuZ3JvdXBzID0gImRyb3AiKSAgIyBEcm9wIGdyb3VwcyBhZnRlciBzdW1tYXJpemF0aW9uCgoKdGFiIDwtIGxlZnRfam9pbihhdXgsIGR0KSB8PgogIHNlbGVjdChwdHNfaWQsIG5fYmlvbWFya2VyX2FmdGVyX2V2ZW50LCB0b3RhbF9iaW9tYXJrZXIsIAogICAgICAgICBkZnNfZGF0ZSwKICAgICAgICAgc3VydmVpbGxhbmNlXzJfZGF0ZTpzdXJ2ZWlsbGFuY2VfMTJfZGF0ZSkgfD4KICBtdXRhdGUoYWNyb3NzKC5jb2xzID0gZGZzX2RhdGU6c3VydmVpbGxhbmNlXzEyX2RhdGUsIAogICAgICAgICAgICAgICAgLmZucyA9IH4gYXNfZGF0ZSgueCkpKSB8PgogIGZpbHRlcihuX2Jpb21hcmtlcl9hZnRlcl9ldmVudCA+IDApCmRhdGF0YWJsZSh0YWIsIGZpbHRlciA9ICJ0b3AiKQoKYXV4IDwtIHRtZXJnZShkYXRhMSA9IGR0X3N1cnZpdmFsLCAKICAgICAgICAgICAgICBkYXRhMiA9IGR0X3N1cnZpdmFsLAogICAgICAgICAgICAgIGlkID0gcHRzX2lkLCAKICAgICAgICAgICAgICBkZnNfZXZlbnQgPSBldmVudChkZnNfdGltZSwgZGZzX2V2ZW50KSkKZHRfZmluYWwgPC0gdG1lcmdlKGRhdGExID0gYXV4LCAKICAgICAgICAgICAgICAgICAgIGRhdGEyID0gZHRfYmlvbWFya2VyLAogICAgICAgICAgICAgICAgICAgaWQgPSBwdHNfaWQsIAogICAgICAgICAgICAgICAgICAgYmlvbWFya2VyX3N0YXR1cyA9IAogICAgICAgICAgICAgICAgICAgICB0ZGMoYmlvbWFya2VyX3RpbWUsIGJpb21hcmtlcl9zdGF0dXMpKQoKZGF0YXRhYmxlKGR0X2ZpbmFsLCBmaWx0ZXIgPSAidG9wIikKCiMgU3ludGF4IGlmIHRoZXJlIGlzIG5vdCB0aW1lLWRlcGVuZGVudCBjb3ZhcmlhdGUKIyBmaXQgPC0gY294cGgoU3VydihkZnNfdGltZSwgZGZzX2V2ZW50KSB+IGJpb21hcmtlcl9zdGF0dXMsCiMgICAgICAgICAgICAgIGRhdGEgPSBkdF9maW5hbCkKIyBzdW1tYXJ5KGZpdCkKCmZpdCA8LSBjb3hwaChTdXJ2KHRzdGFydCwgdHN0b3AsIGRmc19ldmVudCkgfiBiaW9tYXJrZXJfc3RhdHVzLAogICAgICAgICAgICAgZGF0YSA9IGR0X2ZpbmFsKQpzdW1tYXJ5KGZpdCkKY294X2ZpdF9zdW1tYXJ5IDwtIHN1bW1hcnkoZml0KQoKI0V4dHJhY3QgdmFsdWVzIGZvciBIUiwgOTUlIENJLCBhbmQgcC12YWx1ZQpIUiA8LSBjb3hfZml0X3N1bW1hcnkkY29lZmZpY2llbnRzWzJdCmxvd2VyX0NJIDwtIGNveF9maXRfc3VtbWFyeSRjb25mLmludFszXQp1cHBlcl9DSSA8LSBjb3hfZml0X3N1bW1hcnkkY29uZi5pbnRbNF0KcF92YWx1ZSA8LSBjb3hfZml0X3N1bW1hcnkkY29lZmZpY2llbnRzWzVdCmxhYmVsX3RleHQgPC0gcGFzdGUwKCJIUiA9ICIsIHJvdW5kKEhSLCAyKSwgIiAoIiwgcm91bmQobG93ZXJfQ0ksIDIpLCAiLSIsIHJvdW5kKHVwcGVyX0NJLCAyKSwgIik7IHAgPSAiLCByb3VuZChwX3ZhbHVlLCAzKSkKcHJpbnQobGFiZWxfdGV4dCkKYGBgCgoKI01lZGlhbiBudW1iZXJzIG9mIHRpbWUgcG9pbnRzIGluIHRoZSBsb25naXR1ZGluYWwgc2V0dGluZwpgYGB7cn0KIyBMb2FkIHRoZSBkYXRhc2V0CnJtKGxpc3Q9bHMoKSkKc2V0d2QoIn4vRG93bmxvYWRzIikgCmNpcmNfZGF0YSA8LSByZWFkLmNzdigiUldFIFVUVUNfQ2xpbmljYWwgRGF0YSAxMDIwMjUuY3N2IikKY2lyY19kYXRhIDwtIGNpcmNfZGF0YVtjaXJjX2RhdGEkRmluYWwuY29ob3J0PT0iVFJVRSIsXQpjaXJjX2RhdGEgPC0gY2lyY19kYXRhW2NpcmNfZGF0YSRjdEROQS5TdXJ2ZWlsbGFuY2UhPSIiLF0KY2lyY19kYXRhZGYgPC0gYXMuZGF0YS5mcmFtZShjaXJjX2RhdGEpCgptZWRpYW5fTnN1cnZ0cHMgPC0gbWVkaWFuKGNpcmNfZGF0YWRmJE5zdXJ2dHBzLCBuYS5ybSA9IFRSVUUpCm1pbl9Oc3VydnRwcyA8LSBtaW4oY2lyY19kYXRhZGYkTnN1cnZ0cHMsIG5hLnJtID0gVFJVRSkKbWF4X05zdXJ2dHBzIDwtIG1heChjaXJjX2RhdGFkZiROc3VydnRwcywgbmEucm0gPSBUUlVFKQoKY2F0KHNwcmludGYoIk1lZGlhbiAjIG9mIHN1cnZlaWxsYW5jZSB0aW1lIHBvaW50czogJWQgKCVkLSVkKVxuIiwgCiAgICAgICAgICAgIG1lZGlhbl9Oc3VydnRwcywgbWluX05zdXJ2dHBzLCBtYXhfTnN1cnZ0cHMpKQpgYGAKCiNNZWRpYW4gb2YgbWVkaWFuIGludGVydmFsIG9mIGN0RE5BIHRpbWVwb2ludHMgYW5kIHJhZGlvbG9naWNhbCBpbWFnaW5nIGFzc2Vzc21lbnQKYGBge3J9CnJtKGxpc3Q9bHMoKSkKc2V0d2QoIn4vRG93bmxvYWRzIikKZGYgPC0gcmVhZC5jc3YoIlJXRSBVVFVDX09QIDEwMjAyNS5jc3YiLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpCgpuYW1lcyhkZikgPC0gdHJpbXdzKG5hbWVzKGRmKSkKCiMgLS0tLSBIZWxwZXI6IGNvbXB1dGUgbWVkaWFuIGludGVydmFsIHBlciBwYXRpZW50IC0tLS0KbWVkaWFuX2ludGVydmFsX3Blcl9wYXRpZW50IDwtIGZ1bmN0aW9uKGRhdGEsIGZpbHRlcl9jb2wsIGZpbHRlcl92YWwpIHsKICBkYXRhICU+JQogICAgZmlsdGVyKCEhc3ltKGZpbHRlcl9jb2wpID09IGZpbHRlcl92YWwpICU+JQogICAgYXJyYW5nZShQYXRpZW50TmFtZSwgZGF0ZS5kaWZmKSAlPiUKICAgIGdyb3VwX2J5KFBhdGllbnROYW1lKSAlPiUKICAgIG11dGF0ZShpbnRlcnZhbCA9IGRhdGUuZGlmZiAtIGxhZyhkYXRlLmRpZmYpKSAlPiUKICAgIHN1bW1hcmlzZShtZWRpYW5faW50ZXJ2YWwgPSBtZWRpYW4oaW50ZXJ2YWwsIG5hLnJtID0gVFJVRSksIC5ncm91cHMgPSAiZHJvcCIpICU+JQogICAgbXV0YXRlKGV2ZW50X2dyb3VwID0gZmlsdGVyX3ZhbCkKfQoKIyAtLS0tIENvbXB1dGUgcGVyLXBhdGllbnQgbWVkaWFucyAtLS0tCmN0RE5BX3BhdGllbnRfbWVkaWFucyAgIDwtIG1lZGlhbl9pbnRlcnZhbF9wZXJfcGF0aWVudChkZiwgIkV2ZW50IiwgImN0RE5BIikKaW1hZ2luZ19wYXRpZW50X21lZGlhbnMgPC0gbWVkaWFuX2ludGVydmFsX3Blcl9wYXRpZW50KGRmLCAiRXZlbnRfdHlwZSIsICJJbWFnaW5nIikKCiMgLS0tLSBGdW5jdGlvbiB0byBzdW1tYXJpemUgcGF0aWVudC1sZXZlbCBtZWRpYW5zIHRvIGNvaG9ydC1sZXZlbCAtLS0tCmNvaG9ydF9zdW1tYXJ5IDwtIGZ1bmN0aW9uKHBhdGllbnRfZGF0YSwgZXZlbnRfbGFiZWwpIHsKICAjIEZpbHRlciBvdXQgcGF0aWVudHMgd2l0aCBOQSBtZWRpYW4gaW50ZXJ2YWxzCiAgdmFsaWRfZGF0YSA8LSBwYXRpZW50X2RhdGEgJT4lIGZpbHRlcighaXMubmEobWVkaWFuX2ludGVydmFsKSkKICAKICBkYXRhLmZyYW1lKAogICAgRXZlbnRfVHlwZSA9IGV2ZW50X2xhYmVsLAogICAgQ29ob3J0X01lZGlhbl9GcmVxdWVuY3kgPSBtZWRpYW4odmFsaWRfZGF0YSRtZWRpYW5faW50ZXJ2YWwsIG5hLnJtID0gVFJVRSksCiAgICBNaW5fUGF0aWVudF9NZWRpYW4gPSBtaW4odmFsaWRfZGF0YSRtZWRpYW5faW50ZXJ2YWwsIG5hLnJtID0gVFJVRSksCiAgICBNYXhfUGF0aWVudF9NZWRpYW4gPSBtYXgodmFsaWRfZGF0YSRtZWRpYW5faW50ZXJ2YWwsIG5hLnJtID0gVFJVRSksCiAgICBSYW5nZV9QYXRpZW50X01lZGlhbiA9IG1heCh2YWxpZF9kYXRhJG1lZGlhbl9pbnRlcnZhbCwgbmEucm0gPSBUUlVFKSAtIAogICAgICBtaW4odmFsaWRfZGF0YSRtZWRpYW5faW50ZXJ2YWwsIG5hLnJtID0gVFJVRSkKICApCn0KCiMgLS0tLSBDb21iaW5lIGFsbCByZXN1bHRzIC0tLS0KcmVzdWx0cyA8LSBiaW5kX3Jvd3MoCiAgY29ob3J0X3N1bW1hcnkoY3RETkFfcGF0aWVudF9tZWRpYW5zLCAiY3RETkEiKSwKICBjb2hvcnRfc3VtbWFyeShpbWFnaW5nX3BhdGllbnRfbWVkaWFucywgIkltYWdpbmciKQopCgojIC0tLS0gUHJpbnQgY29ob3J0LWxldmVsIHN1bW1hcnkgLS0tLQpjYXQoIj09PT09IE1lZGlhbiBGcmVxdWVuY3kgKERheXMpIHBlciBDb2hvcnQgPT09PT1cbiIpCnByaW50KHJlc3VsdHMsIHJvdy5uYW1lcyA9IEZBTFNFKQoKIyAtLS0tIE9QVElPTkFMOiBTYXZlIHBhdGllbnQtbGV2ZWwgbWVkaWFucyAtLS0tCnBhdGllbnRfbGV2ZWxfbWVkaWFucyA8LSBiaW5kX3Jvd3MoY3RETkFfcGF0aWVudF9tZWRpYW5zLCBpbWFnaW5nX3BhdGllbnRfbWVkaWFucykKI3dyaXRlLmNzdihwYXRpZW50X2xldmVsX21lZGlhbnMsICJwYXRpZW50X21lZGlhbl9pbnRlcnZhbHMuY3N2Iiwgcm93Lm5hbWVzID0gRkFMU0UpCmBgYAoKI1Bsb3QgZm9yIGluZGl2aWR1YWwgbGVhZC10aW1lIGNhbGN1bGF0aW9ucyBmb3IgZWFjaCBwdApgYGB7cn0Kcm0obGlzdD1scygpKQpjc3ZfcGF0aCA8LSAifi9Eb3dubG9hZHMvQ0xJQSBVVFVDX0xlYWQgVGltZSBwdHMuY3N2IgpkZiA8LSByZWFkLmNzdihjc3ZfcGF0aCwgY2hlY2submFtZXMgPSBGQUxTRSkKaWYgKCJGaW5hbC5jb2hvcnQiICVpbiUgbmFtZXMoZGYpKSB7CiAgZGYgPC0gZGZbZGYkRmluYWwuY29ob3J0ID09IFRSVUUsIF0KfSBlbHNlIHsKICB3YXJuaW5nKCJDb2x1bW4gJ0ZpbmFsLmNvaG9ydCcgbm90IGZvdW5kIGluIHRoZSBkYXRhc2V0LiIpCn0KaWRfY2FuZGlkYXRlcyA8LSBjKCJQYXRpZW50IiwiSUQiLCJQdCIsIlN1YmplY3QiLCJTYW1wbGUiKQppZF9jb2wgPC0gaWRfY2FuZGlkYXRlc1tpZF9jYW5kaWRhdGVzICVpbiUgbmFtZXMoZGYpXVsxXQppZiAoaXMubmEoaWRfY29sKSkgewogIGRmIDwtIGRmICU+JSBtdXRhdGUoUGF0aWVudCA9IHJvd19udW1iZXIoKSkKICBpZF9jb2wgPC0gIlBhdGllbnQiCn0KCm51bV9jb2xzIDwtIGludGVyc2VjdChjKCJNUiIsIkNSIiwiTFQiKSwgbmFtZXMoZGYpKQpkZltudW1fY29sc10gPC0gbGFwcGx5KGRmW251bV9jb2xzXSwgZnVuY3Rpb24oeCkgc3VwcHJlc3NXYXJuaW5ncyhhcy5udW1lcmljKHgpKSkKCiMgSWYgTFQgbWlzc2luZywgY29tcHV0ZSBpbiBkYXlzCmlmICghKCJMVCIgJWluJSBuYW1lcyhkZikpKSB7CiAgZGYgPC0gZGYgJT4lIG11dGF0ZShMVCA9IENSIC0gTVIpCn0KCiMgLS0tLSBjb252ZXJ0IHRvIG1vbnRocyAtLS0tCiMgQXBwcm94aW1hdGU6IDMwLjQ0IGRheXMgcGVyIG1vbnRoIChhdmVyYWdlKQpkYXlzX3RvX21vbnRocyA8LSBmdW5jdGlvbih4KSB4IC8gMzAuNDM3CgpkZiA8LSBkZiAlPiUKICBtdXRhdGUoTVJfbW8gPSBkYXlzX3RvX21vbnRocyhNUiksCiAgICAgICAgIENSX21vID0gZGF5c190b19tb250aHMoQ1IpLAogICAgICAgICBMVF9tbyA9IGRheXNfdG9fbW9udGhzKExUKSkKCiMgLS0tLSBvcmRlcmluZyBmb3IgeS1heGlzIC0tLS0KZGYgPC0gZGYgJT4lCiAgbXV0YXRlKEVhcmxpZXN0ID0gcG1pbihNUl9tbywgQ1JfbW8sIG5hLnJtID0gVFJVRSkpICU+JQogIGFycmFuZ2UoRWFybGllc3QpICU+JQogIG11dGF0ZShQYXRpZW50X2YgPSBmYWN0b3IoLmRhdGFbW2lkX2NvbF1dLCBsZXZlbHMgPSAuZGF0YVtbaWRfY29sXV0pKQoKIyAtLS0tIGxvbmcgZm9ybWF0IGZvciBwb2ludHMgLS0tLQpwb2ludHNfbG9uZyA8LSBkZiAlPiUKICBzZWxlY3QoUGF0aWVudF9mLCBNUl9tbywgQ1JfbW8pICU+JQogIHBpdm90X2xvbmdlcihjKE1SX21vLCBDUl9tbyksIG5hbWVzX3RvID0gIlR5cGUiLCB2YWx1ZXNfdG8gPSAiTW9udGhzIikgJT4lCiAgbXV0YXRlKFR5cGUgPSBkcGx5cjo6cmVjb2RlKFR5cGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJNUl9tbyIgPSAiTW9sZWN1bGFyIHJlY3VycmVuY2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQ1JfbW8iID0gIkNsaW5pY2FsIHJlY3VycmVuY2UiKSkKCiMgLS0tLSBzZWdtZW50cyBiZXR3ZWVuIE1SIGFuZCBDUiAtLS0tCnNlZ21lbnRzX2RmIDwtIGRmICU+JQogIHRyYW5zbXV0ZShQYXRpZW50X2YsCiAgICAgICAgICAgIHgwID0gTVJfbW8sIHgxID0gQ1JfbW8sCiAgICAgICAgICAgIGx0X2ZsYWcgPSBpZl9lbHNlKExUID4gMTIwLCAiZ3QxMjAiLCAibGUxMjAiKSkKCiMgLS0tLSBhbm5vdGF0aW9uIC0tLS0KbWVkX2x0ICA8LSBtZWRpYW4oZGYkTFRfbW8sIG5hLnJtID0gVFJVRSkKbWluX2x0ICA8LSBtaW4oZGYkTFRfbW8sIG5hLnJtID0gVFJVRSkKbWF4X2x0ICA8LSBtYXgoZGYkTFRfbW8sIG5hLnJtID0gVFJVRSkKCmFubm90X2xhYmVsIDwtIHNwcmludGYoIk1lZGlhbiBsZWFkLXRpbWU6XG4lLjFmIG1vbnRocyAoJS4xZiB0byAlLjFmKSIsCiAgICAgICAgICAgICAgICAgICAgICAgbWVkX2x0LCBtaW5fbHQsIG1heF9sdCkKCiMgLS0tLSBwbG90IC0tLS0KcGFsIDwtIGMoIk1vbGVjdWxhciByZWN1cnJlbmNlIiA9ICIjMTBCNEMxIiwKICAgICAgICAgIkNsaW5pY2FsIHJlY3VycmVuY2UiICA9ICIjQzk2QTcyIikKCnhfbWF4IDwtIG1heChjKGRmJE1SX21vLCBkZiRDUl9tbyksIG5hLnJtID0gVFJVRSkKeV9taWQgPC0gbGV2ZWxzKGRmJFBhdGllbnRfZilbY2VpbGluZyhubGV2ZWxzKGRmJFBhdGllbnRfZikgKiAwLjU1KV0KCnAgPC0gZ2dwbG90KCkgKwogIGdlb21fc2VnbWVudChkYXRhID0gc2VnbWVudHNfZGYsCiAgICAgICAgICAgICAgIGFlcyh4ID0geDAsIHhlbmQgPSB4MSwgeSA9IFBhdGllbnRfZiwgeWVuZCA9IFBhdGllbnRfZiwKICAgICAgICAgICAgICAgICAgIGxpbmV0eXBlID0gbHRfZmxhZyksCiAgICAgICAgICAgICAgIGxpbmV3aWR0aCA9IDAuNiwgY29sb3IgPSAiYmxhY2siKSArCiAgc2NhbGVfbGluZXR5cGVfbWFudWFsKHZhbHVlcyA9IGMobGUxMjAgPSAic29saWQiLCBndDEyMCA9ICJkYXNoZWQiKSwKICAgICAgICAgICAgICAgICAgICAgICAgZ3VpZGUgPSAibm9uZSIpICsKICBnZW9tX3BvaW50KGRhdGEgPSBwb2ludHNfbG9uZywKICAgICAgICAgICAgIGFlcyh4ID0gTW9udGhzLCB5ID0gUGF0aWVudF9mLCBjb2xvciA9IFR5cGUpLAogICAgICAgICAgICAgc2l6ZSA9IDIuOCkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBwYWwsIG5hbWUgPSBOVUxMKSArCiAgbGFicyh4ID0gIk1vbnRocyBmcm9tIFN1cmdlcnkgb3IgRGVmaW5pdGl2ZSBUcmVhdG1lbnQiLCB5ID0gTlVMTCkgKwogIGFubm90YXRlKCJ0ZXh0IiwKICAgICAgICAgICB4ID0geF9tYXggKiAwLjczLAogICAgICAgICAgIHkgPSB5X21pZCwKICAgICAgICAgICBsYWJlbCA9IGFubm90X2xhYmVsLAogICAgICAgICAgIGhqdXN0ID0gMCwgdmp1c3QgPSAwLjUsIHNpemUgPSAzLjgpICsKICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsIGNlaWxpbmcoeF9tYXgvMykqMywgYnkgPSAzKSkgKwogIGNvb3JkX2NhcnRlc2lhbih4bGltID0gYygwLCB4X21heCAqIDEuMDUpKSArCiAgdGhlbWVfYncoYmFzZV9zaXplID0gMTIpICsKICB0aGVtZSgKICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2xpbmUoY29sb3IgPSAiZ3JleTg1IiwgbGluZXdpZHRoID0gMC4zKSwKICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2xpbmUoY29sb3IgPSAiZ3JleTkyIiwgbGluZXdpZHRoID0gMC4yKSwKICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSA5KQogICkKCnByaW50KHApCgp3Y19kZiA8LSBkZiAlPiUgZHBseXI6OnNlbGVjdChNUiwgQ1IpICU+JSB0aWR5cjo6ZHJvcF9uYSgpCnBhaXJlZF9kaWZmIDwtIHdjX2RmJENSIC0gd2NfZGYkTVIKCndpbHggPC0gd2lsY294LnRlc3QoCiAgeCA9IHdjX2RmJENSLCB5ID0gd2NfZGYkTVIsCiAgcGFpcmVkID0gVFJVRSwKICBhbHRlcm5hdGl2ZSA9ICJ0d28uc2lkZWQiLAogIGNvbmYuaW50ID0gVFJVRSwgICAgICAjIGdpdmVzIENJIGZvciB0aGUgSG9kZ2Vz4oCTTGVobWFubiBlc3RpbWF0ZSBvZiB0aGUgbWVkaWFuIGRpZmZlcmVuY2UKICBleGFjdCA9IEZBTFNFICAgICAgICAgIyBzYWZlciBmb3IgdGllcy9sYXJnZSBOCikKCldfc3RhdCAgIDwtIHVubmFtZSh3aWx4JHN0YXRpc3RpYykgICAgICAgICAgIyBXaWxjb3hvbiBWCnBfdmFsdWUgIDwtIHdpbHgkcC52YWx1ZQpITF9lc3QgICA8LSB1bm5hbWUod2lseCRlc3RpbWF0ZSkgICAgICAgICAgICMgbWVkaWFuIG9mIChDUiAtIE1SKQpITF9jaSAgICA8LSB3aWx4JGNvbmYuaW50ICAgICAgICAgICAgICAgICAgICMgQ0kgZm9yIG1lZGlhbiBkaWZmZXJlbmNlCgojIFNpbXBsZSBwLXZhbHVlIGZvcm1hdHRlciBmb3IgYW5ub3RhdGlvbi9wdWJsaWNhdGlvbgpmbXRfcCA8LSBmdW5jdGlvbihwKSB7CiAgaWYgKGlzLm5hKHApKSByZXR1cm4oIk5BIikKICBpZiAocCA8IDAuMDAxKSAiPCAwLjAwMSIgZWxzZSBzcHJpbnRmKCI9ICUuM2YiLCByb3VuZChwLCAzKSkKfQpwX3RleHQgPC0gcGFzdGUwKCJQICIsIGZtdF9wKHBfdmFsdWUpKSAgIyBlLmcuLCAiUCA9IDAuMDAzIiBvciAiUCA8IDAuMDAxIgoKIyBPcHRpb25hbDogcHJpbnQgYSBjb21wYWN0IHN1bW1hcnkKY2F0KCJcblBhaXJlZCBXaWxjb3hvbiBzaWduZWQtcmFuayB0ZXN0IChDUiB2cyBNUilcbiIsCiAgICAiLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuIiwKICAgIHNwcmludGYoIk4gcGFpcnM6ICVkIiwgbnJvdyh3Y19kZikpLCAiXG4iLAogICAgc3ByaW50ZigiVyAoVik6ICVnIiwgV19zdGF0KSwgIlxuIiwKICAgIHNwcmludGYoIlAtdmFsdWU6ICVzIiwgaWZlbHNlKHBfdmFsdWUgPCAwLjAwMSwgIjwgMC4wMDEiLCBzcHJpbnRmKCIlLjZmIiwgcF92YWx1ZSkpKSwgIlxuIiwKICAgIHNwcmludGYoIkhvZGdlc+KAk0xlaG1hbm4gbWVkaWFuIGRpZmZlcmVuY2UgKENSIC0gTVIpOiAlLjFmIGRheXMiLCBITF9lc3QpLCAiXG4iLAogICAgc3ByaW50ZigiOTUlJSBDSTogWyUuMWYsICUuMWZdIGRheXMiLCBITF9jaVsxXSwgSExfY2lbMl0pLCAiXG4iLCBzZXAgPSAiIikKYGBgCgoKI011bHRpdmFyaWF0ZSBjb3ggcmVncmVzc2lvbiBhdCBTdXJ2ZWlsbGFuY2UgV2luZG93IGZvciBSRlMKYGBge3J9CnJtKGxpc3Q9bHMoKSkKc2V0d2QoIn4vRG93bmxvYWRzIikgCmNpcmNfZGF0YSA8LSByZWFkLmNzdigiUldFIFVUVUNfQ2xpbmljYWwgRGF0YSAxMDIwMjUuY3N2IikKY2lyY19kYXRhIDwtIGNpcmNfZGF0YVtjaXJjX2RhdGEkRmluYWwuY29ob3J0PT0iVFJVRSIsXQpjaXJjX2RhdGEgPC0gY2lyY19kYXRhW2NpcmNfZGF0YSRjdEROQS5TdXJ2ZWlsbGFuY2UhPSIiLF0KCmNpcmNfZGF0YSRjdEROQS5TdXJ2ZWlsbGFuY2UgPC0gZmFjdG9yKGNpcmNfZGF0YSRjdEROQS5TdXJ2ZWlsbGFuY2UsIGxldmVscz1jKCJORUdBVElWRSIsIlBPU0lUSVZFIiksIGxhYmVscyA9IGMoIk5lZ2F0aXZlIiwgIlBvc2l0aXZlIikpCmNpcmNfZGF0YSRjU3RhZ2UgPC0gZmFjdG9yKGNpcmNfZGF0YSRjU3RhZ2UsIGxldmVscyA9IGMoIjAvSSIsICJJSS9JSUkiKSkKY2lyY19kYXRhJE5BQyA8LSBmYWN0b3IoY2lyY19kYXRhJE5BQywgbGV2ZWxzID0gYygiRkFMU0UiLCAiVFJVRSIpKQpjaXJjX2RhdGEkQUNUIDwtIGZhY3RvcihjaXJjX2RhdGEkQUNULCBsZXZlbHMgPSBjKCJGQUxTRSIsICJUUlVFIikpCnN1cnZfb2JqZWN0IDwtIFN1cnYodGltZSA9IGNpcmNfZGF0YSRSRlMubW9udGhzLCBldmVudCA9IGNpcmNfZGF0YSRSRlMuRXZlbnQpIApjb3hfZml0IDwtIGNveHBoKHN1cnZfb2JqZWN0IH4gY3RETkEuU3VydmVpbGxhbmNlICsgQWdlICsgY1N0YWdlICsgTkFDICsgQUNULCBkYXRhPWNpcmNfZGF0YSkgCmdnZm9yZXN0KGNveF9maXQsIGRhdGEgPSBjaXJjX2RhdGEsIG1haW4gPSAiTXVsdGl2YXJpYXRlIFJlZ3Jlc3Npb24gTW9kZWwgZm9yIFJGUyAtIEFsbCBTdGFnZXMiLCByZWZMYWJlbCA9ICJSZWZlcmVuY2UgR3JvdXAiKQp0ZXN0LnBoIDwtIGNveC56cGgoY294X2ZpdCkKYGBgCgojY3RETkEgYW5kIE1UTS9tTCBEeW5hbWljcyBmb3IgcHRzIGF0IHN1cnZlaWxsYW5jZSB3aW5kb3cKYGBge3J9CiNEeW5hbWljcyBhbmQgTVRNL21MIHBsb3RzIGZvciBwYXRpZW50cyB3aXRoIGN0RE5BIG5lZ2F0aXZlIGF0IHN1cnZlaWxsYW5jZQpybShsaXN0PWxzKCkpCnNldHdkKCJ+L0Rvd25sb2FkcyIpCmRmIDwtIHJlYWQuY3N2KCJDTElBIFVUVUMgY3RETkEgTVRNLmNzdiIsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkKZGYgPC0gZGZbZGYkRmluYWwuY29ob3J0PT0iVFJVRSIsXQpkZiA8LSBkZltkZiRjdEROQS5TdXJ2ZWlsbGFuY2U9PSJORUdBVElWRSIsXQoKZGYkUkZTLkV2ZW50IDwtIGlmZWxzZShkZiRSRlMuRXZlbnQgJWluJSBjKCJObyIsICJubyIsICJGQUxTRSIsICJGYWxzZSIsICIwIiksIEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShkZiRSRlMuRXZlbnQgJWluJSBjKCJZZXMiLCAieWVzIiwgIlRSVUUiLCAiVHJ1ZSIsICIxIiksIFRSVUUsIE5BKSkKZGYkUkZTLkV2ZW50IDwtIGZhY3RvcihkZiRSRlMuRXZlbnQsIGxldmVscyA9IGMoRkFMU0UsIFRSVUUpKQpkZiA8LSBkZiAlPiUKICBncm91cF9ieShQYXRpZW50TmFtZSkgJT4lCiAgZmlsdGVyKG4oKSA+PSAyKSAlPiUgI2tlZXAgb25seSBwdHMgd2l0aCBhdCBsZWFzdCAyIHBvc3Qtc3VyZ2VyeSB0aW1lIHBvaW50cwogIHVuZ3JvdXAoKQoKbnVtX3VuaXF1ZSA8LSBsZW5ndGgodW5pcXVlKGRmJFBhdGllbnROYW1lKSkKY2F0KCJOdW1iZXIgb2YgdW5pcXVlIHBhdGllbnRzOiIsIG51bV91bmlxdWUsICJcbiIpCgpkZl9wYXRpZW50X3BmcyA8LSBkZiAlPiUKICBncm91cF9ieShQYXRpZW50TmFtZSkgJT4lCiAgZHBseXI6OnN1bW1hcml6ZSgKICAgIFBGU19UcnVlID0gYW55KFJGUy5FdmVudCA9PSBUUlVFLCBuYS5ybSA9IFRSVUUpLAogICAgUEZTX0ZhbHNlID0gYWxsKFJGUy5FdmVudCA9PSBGQUxTRSwgbmEucm0gPSBUUlVFKQogICkKCm51bV90cnVlIDwtIHN1bShkZl9wYXRpZW50X3BmcyRQRlNfVHJ1ZSkKbnVtX2ZhbHNlIDwtIHN1bShkZl9wYXRpZW50X3BmcyRQRlNfRmFsc2UpCgpjYXQoIk51bWJlciBvZiB1bmlxdWUgcGF0aWVudHMgd2l0aCBFdmVudDoiLCBudW1fdHJ1ZSwgIlxuIikKY2F0KCJOdW1iZXIgb2YgdW5pcXVlIHBhdGllbnRzIHdpdGggTm8gRXZlbnQ6IiwgbnVtX2ZhbHNlLCAiXG4iKQoKcCA8LSBnZ3Bsb3QoZGYsIGFlcyh4ID0gZGF0ZS5kaWZmLm1vbnRocywgCiAgICAgICAgICAgICAgICAgICAgeSA9IE1UTS5tTCwgCiAgICAgICAgICAgICAgICAgICAgZ3JvdXAgPSBQYXRpZW50TmFtZSwgCiAgICAgICAgICAgICAgICAgICAgY29sb3IgPSBSRlMuRXZlbnQpKSArCiAgZ2VvbV9saW5lKCkgKyAgICAgICMgQ29ubmVjdCB0aW1lcG9pbnRzIGZvciBlYWNoIHBhdGllbnQKICBnZW9tX3BvaW50KCkgKyAgICAgIyBBZGQgcG9pbnRzIGZvciBlYWNoIHRpbWVwb2ludAogICMgVXNlIGEgbG9nMTAgc2NhbGUgZm9yIHRoZSB5LWF4aXMgd2l0aCBzcGVjaWZpZWQgYnJlYWtzCiAgc2NhbGVfeV9sb2cxMChicmVha3MgPSBjKDAuMDEsIDAuMSwgMSwgMTAsIDEwMCwgMTAwMCksCiAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCIwLjAxIiwiMC4xIiwgIjEiLCAiMTAiLCAiMTAwIiwgIjEwMDAiKSkgKwogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoMCwgbWF4KGRmJGRhdGUuZGlmZi5tb250aHMsIG5hLnJtID0gVFJVRSksIGJ5ID0gNikpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiRkFMU0UiID0gImJsdWUiLCAiVFJVRSIgPSAicmVkIikpICsKICBsYWJzKAogICAgeCA9ICJUaW1lIFNpbmNlIFN1cmdlcnkgb3Igc3RhcnQgb2YgZGVmaW5pdGl2ZSB0cmVhdG1lbnQgKG1vbnRocykiLAogICAgeSA9ICJNZWFuIFR1bW9yIE1vbGVjdWxlcyBwZXIgbUwgKE1UTS9tTCkiLAogICAgY29sb3IgPSAiUkZTIEV2ZW50IgogICkgKwogIHRoZW1lX21pbmltYWwoKQpwcmludChwKQoKI0R5bmFtaWNzIGFuZCBNVE0vbUwgcGxvdHMgZm9yIHBhdGllbnRzIHdpdGggY3RETkEgcG9zaXRpdmUgYXQgc3VydmVpbGxhbmNlCnJtKGxpc3Q9bHMoKSkKc2V0d2QoIn4vRG93bmxvYWRzIikKZGYgPC0gcmVhZC5jc3YoIkNMSUEgVVRVQyBjdEROQSBNVE0uY3N2Iiwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQpkZiA8LSBkZltkZiRGaW5hbC5jb2hvcnQ9PSJUUlVFIixdCmRmIDwtIGRmW2RmJGN0RE5BLlN1cnZlaWxsYW5jZT09IlBPU0lUSVZFIixdCgpkZiRSRlMuRXZlbnQgPC0gaWZlbHNlKGRmJFJGUy5FdmVudCAlaW4lIGMoIk5vIiwgIm5vIiwgIkZBTFNFIiwgIkZhbHNlIiwgIjAiKSwgRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKGRmJFJGUy5FdmVudCAlaW4lIGMoIlllcyIsICJ5ZXMiLCAiVFJVRSIsICJUcnVlIiwgIjEiKSwgVFJVRSwgTkEpKQpkZiRSRlMuRXZlbnQgPC0gZmFjdG9yKGRmJFJGUy5FdmVudCwgbGV2ZWxzID0gYyhGQUxTRSwgVFJVRSkpCmRmIDwtIGRmICU+JQogIGdyb3VwX2J5KFBhdGllbnROYW1lKSAlPiUKICBmaWx0ZXIobigpID49IDIpICU+JSAja2VlcCBvbmx5IHB0cyB3aXRoIGF0IGxlYXN0IDIgcG9zdC1zdXJnZXJ5IHRpbWUgcG9pbnRzCiAgdW5ncm91cCgpCgpudW1fdW5pcXVlIDwtIGxlbmd0aCh1bmlxdWUoZGYkUGF0aWVudE5hbWUpKQpjYXQoIk51bWJlciBvZiB1bmlxdWUgcGF0aWVudHM6IiwgbnVtX3VuaXF1ZSwgIlxuIikKCmRmX3BhdGllbnRfcGZzIDwtIGRmICU+JQogIGdyb3VwX2J5KFBhdGllbnROYW1lKSAlPiUKICBkcGx5cjo6c3VtbWFyaXplKAogICAgUEZTX1RydWUgPSBhbnkoUkZTLkV2ZW50ID09IFRSVUUsIG5hLnJtID0gVFJVRSksCiAgICBQRlNfRmFsc2UgPSBhbGwoUkZTLkV2ZW50ID09IEZBTFNFLCBuYS5ybSA9IFRSVUUpCiAgKQoKbnVtX3RydWUgPC0gc3VtKGRmX3BhdGllbnRfcGZzJFBGU19UcnVlKQpudW1fZmFsc2UgPC0gc3VtKGRmX3BhdGllbnRfcGZzJFBGU19GYWxzZSkKCmNhdCgiTnVtYmVyIG9mIHVuaXF1ZSBwYXRpZW50cyB3aXRoIEV2ZW50OiIsIG51bV90cnVlLCAiXG4iKQpjYXQoIk51bWJlciBvZiB1bmlxdWUgcGF0aWVudHMgd2l0aCBObyBFdmVudDoiLCBudW1fZmFsc2UsICJcbiIpCgpwIDwtIGdncGxvdChkZiwgYWVzKHggPSBkYXRlLmRpZmYubW9udGhzLCAKICAgICAgICAgICAgICAgICAgICB5ID0gTVRNLm1MLCAKICAgICAgICAgICAgICAgICAgICBncm91cCA9IFBhdGllbnROYW1lLCAKICAgICAgICAgICAgICAgICAgICBjb2xvciA9IFJGUy5FdmVudCkpICsKICBnZW9tX2xpbmUoKSArICAgICAgIyBDb25uZWN0IHRpbWVwb2ludHMgZm9yIGVhY2ggcGF0aWVudAogIGdlb21fcG9pbnQoKSArICAgICAjIEFkZCBwb2ludHMgZm9yIGVhY2ggdGltZXBvaW50CiAgIyBVc2UgYSBsb2cxMCBzY2FsZSBmb3IgdGhlIHktYXhpcyB3aXRoIHNwZWNpZmllZCBicmVha3MKICBzY2FsZV95X2xvZzEwKGJyZWFrcyA9IGMoMC4wMSwgMC4xLCAxLCAxMCwgMTAwLCAxMDAwKSwKICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIjAuMDEiLCIwLjEiLCAiMSIsICIxMCIsICIxMDAiLCAiMTAwMCIpKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLCBtYXgoZGYkZGF0ZS5kaWZmLm1vbnRocywgbmEucm0gPSBUUlVFKSwgYnkgPSA2KSkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJGQUxTRSIgPSAiYmx1ZSIsICJUUlVFIiA9ICJyZWQiKSkgKwogIGxhYnMoCiAgICB4ID0gIlRpbWUgU2luY2UgU3VyZ2VyeSBvciBzdGFydCBvZiBkZWZpbml0aXZlIHRyZWF0bWVudCAobW9udGhzKSIsCiAgICB5ID0gIk1lYW4gVHVtb3IgTW9sZWN1bGVzIHBlciBtTCAoTVRNL21MKSIsCiAgICBjb2xvciA9ICJSRlMgRXZlbnQiCiAgKSArCiAgdGhlbWVfbWluaW1hbCgpCnByaW50KHApCmBgYAoKI2N0RE5BIHZlbG9jaXR5IGFuZCBsZWFkIHRpbWUgbGluZXIgcmVncmVzc2lvbgpgYGB7cn0Kcm0obGlzdD1scygpKQpjc3ZfcGF0aCA8LSAifi9Eb3dubG9hZHMvQ0xJQSBVVFVDX2N0RE5BIHZlbG9jaXR5LmNzdiIgICMgPC0gYWRqdXN0IGlmIG5lZWRlZApkZl9yYXcgPC0gcmVhZC5jc3YoY3N2X3BhdGgsIGNoZWNrLm5hbWVzID0gRkFMU0UpCmRmIDwtIGRmX3JhdyAlPiUKICByZW5hbWUoTVRNX21MID0gYE1UTS5tTGApICU+JQogIG11dGF0ZSgKICAgIGRheXNDUi5tb250aHMgPSBhcy5udW1lcmljKGRheXNDUi5tb250aHMpLAogICAgTVRNX21MID0gYXMubnVtZXJpYyhNVE1fbUwpCiAgKSAlPiUKICBmaWx0ZXIoRmluYWwuY29ob3J0ID09IFRSVUUsICFpcy5uYShQYXRpZW50TmFtZSksICFpcy5uYShkYXlzQ1IubW9udGhzKSwgIWlzLm5hKE1UTV9tTCkpCgpwcmVDUiA8LSBkZiAlPiUKICBmaWx0ZXIoZGF5c0NSLm1vbnRocyA8PSAwLCBpcy5maW5pdGUoTVRNX21MKSwgTVRNX21MID4gMCkKCmVsaWdpYmxlIDwtIHByZUNSICU+JQogIGdyb3VwX2J5KFBhdGllbnROYW1lKSAlPiUKICBmaWx0ZXIobigpID49IDIsIG5fZGlzdGluY3QoZGF5c0NSLm1vbnRocykgPj0gMikgJT4lCiAgdW5ncm91cCgpCmlmIChucm93KGVsaWdpYmxlKSA9PSAwKSB7CiAgd2FybmluZygiTm8gcGF0aWVudHMgaGF2ZSDiiaUyIHZhbGlkIHByZS1yZWN1cnJlbmNlIHBvaW50cyB3aXRoIGRpc3RpbmN0IHRpbWVzOyByZWdyZXNzaW9uIGxpbmVzIHdpbGwgYmUgb21pdHRlZC4iKQp9CgpmaXRzIDwtIGVsaWdpYmxlICU+JQogIGdyb3VwX2J5KFBhdGllbnROYW1lKSAlPiUKICBzdW1tYXJpc2UoeF9taW4gPSBtaW4oZGF5c0NSLm1vbnRocywgbmEucm0gPSBUUlVFKSwgLmdyb3VwcyA9ICJkcm9wIikgJT4lCiAgbXV0YXRlKGdyaWQgPSBtYXAoeF9taW4sIH5zZXEoLngsIDAsIGxlbmd0aC5vdXQgPSA1MCkpKSAlPiUKICBzZWxlY3QoUGF0aWVudE5hbWUsIGdyaWQpCgpwcmVkaWN0X3BhdGllbnQgPC0gZnVuY3Rpb24oZGF0LCBuZXd4KSB7CiAgaWYgKGxlbmd0aChuZXd4KSA9PSAwKSB7CiAgICByZXR1cm4odGliYmxlKGRheXNDUi5tb250aHMgPSBudW1lcmljKDApLCBNVE1fbUwgPSBudW1lcmljKDApKSkKICB9CiAgZGF0MiA8LSBkYXQgJT4lCiAgICBmaWx0ZXIoaXMuZmluaXRlKE1UTV9tTCksIE1UTV9tTCA+IDApICU+JQogICAgbXV0YXRlKGxvZ19jdGRuYSA9IGxvZzEwKE1UTV9tTCkpCiAgaWYgKG5yb3coZGF0MikgPCAyIHx8IG5fZGlzdGluY3QoZGF0MiRkYXlzQ1IubW9udGhzKSA8IDIgfHwgYW55KCFpcy5maW5pdGUoZGF0MiRsb2dfY3RkbmEpKSkgewogICAgcmV0dXJuKHRpYmJsZShkYXlzQ1IubW9udGhzID0gbnVtZXJpYygwKSwgTVRNX21MID0gbnVtZXJpYygwKSkpCiAgfQogIG0gPC0gbG0obG9nX2N0ZG5hIH4gZGF5c0NSLm1vbnRocywgZGF0YSA9IGRhdDIpCiAgdGliYmxlKAogICAgZGF5c0NSLm1vbnRocyA9IG5ld3gsCiAgICBNVE1fbUwgPSAxMCBeIHByZWRpY3QobSwgbmV3ZGF0YSA9IHRpYmJsZShkYXlzQ1IubW9udGhzID0gbmV3eCkpCiAgKQp9CgpwcmVkX2xpbmVzIDwtIGVsaWdpYmxlICU+JQogIGdyb3VwX2J5KFBhdGllbnROYW1lKSAlPiUKICB0aWR5cjo6bmVzdCgpICU+JSAgICAgICAgICAgICAgICAgICAgICAgIyBsaXN0LWNvbHVtbiAiZGF0YSIgcGVyIHBhdGllbnQKICBsZWZ0X2pvaW4oZml0cywgYnkgPSAiUGF0aWVudE5hbWUiKSAlPiUgIyBsaXN0LWNvbHVtbiAiZ3JpZCIgcGVyIHBhdGllbnQKICBtdXRhdGUocHJlZCA9IG1hcDIoZGF0YSwgZ3JpZCwgfnByZWRpY3RfcGF0aWVudCgueCwgLnkpKSkgJT4lCiAgc2VsZWN0KFBhdGllbnROYW1lLCBwcmVkKSAlPiUKICB0aWR5cjo6dW5uZXN0KHByZWQpCgpwb29sZWRfbGluZSA8LSB7CiAgaWYgKG5yb3cocHJlQ1IpID49IDIgJiYgbl9kaXN0aW5jdChwcmVDUiRkYXlzQ1IubW9udGhzKSA+PSAyKSB7CiAgICBwb29sZWRfeCA8LSBzZXEobWluKHByZUNSJGRheXNDUi5tb250aHMsIG5hLnJtID0gVFJVRSksIDAsIGxlbmd0aC5vdXQgPSAxMDApCiAgICBwb29sZWRfZml0IDwtIGxtKGxvZzEwKE1UTV9tTCkgfiBkYXlzQ1IubW9udGhzLCBkYXRhID0gcHJlQ1IpCiAgICB0aWJibGUoCiAgICAgIGRheXNDUi5tb250aHMgPSBwb29sZWRfeCwKICAgICAgTVRNX21MID0gMTAgXiBwcmVkaWN0KHBvb2xlZF9maXQsIG5ld2RhdGEgPSB0aWJibGUoZGF5c0NSLm1vbnRocyA9IHBvb2xlZF94KSkKICAgICkKICB9IGVsc2UgewogICAgdGliYmxlKGRheXNDUi5tb250aHMgPSBudW1lcmljKDApLCBNVE1fbUwgPSBudW1lcmljKDApKQogIH0KfQoKeF9taW4gPC0gZmxvb3IobWluKGRmJGRheXNDUi5tb250aHMsIG5hLnJtID0gVFJVRSkgLyAzKSAqIDMKeF9tYXggPC0gY2VpbGluZyhtYXgoZGYkZGF5c0NSLm1vbnRocywgbmEucm0gPSBUUlVFKSAvIDMpICogMwp5X21pbl9wb3MgPC0gbWF4KG1pbihkZiRNVE1fbUxbZGYkTVRNX21MID4gMF0sIG5hLnJtID0gVFJVRSkgLyAyLCAwLjAxKQp5X21heF9wb3MgPC0gMTAgXiBjZWlsaW5nKGxvZzEwKG1heChkZiRNVE1fbUwsIG5hLnJtID0gVFJVRSkpKQpsb2dfYnJlYWtzIDwtIDEwIF4gc2VxKGZsb29yKGxvZzEwKHlfbWluX3BvcykpLCBjZWlsaW5nKGxvZzEwKHlfbWF4X3BvcykpKQoKcCA8LSBnZ3Bsb3QoKSArCiAgIyBwZXItcGF0aWVudCBmaXR0ZWQgbGluZXMgKGlmIGFueSkKICBnZW9tX2xpbmUoZGF0YSA9IHByZWRfbGluZXMsCiAgICAgICAgICAgIGFlcyh4ID0gZGF5c0NSLm1vbnRocywgeSA9IE1UTV9tTCwgY29sb3IgPSBQYXRpZW50TmFtZSksCiAgICAgICAgICAgIGxpbmV3aWR0aCA9IDEpICsKICAjIHBvb2xlZCBkYXNoZWQgdHJlbmQgKGlmIGFueSkKICBnZW9tX2xpbmUoZGF0YSA9IHBvb2xlZF9saW5lLAogICAgICAgICAgICBhZXMoeCA9IGRheXNDUi5tb250aHMsIHkgPSBNVE1fbUwpLAogICAgICAgICAgICBsaW5ld2lkdGggPSAwLjgsIGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbG9yID0gImdyZXkzNSIpICsKICAjIHJhdyBwb2ludHMgKGFsbCB0aW1lcG9pbnRzLCBiZWZvcmUgYW5kIGFmdGVyKQogIGdlb21fcG9pbnQoZGF0YSA9IGRmLAogICAgICAgICAgICAgYWVzKHggPSBkYXlzQ1IubW9udGhzLCB5ID0gTVRNX21MLCBjb2xvciA9IFBhdGllbnROYW1lKSwKICAgICAgICAgICAgIHNpemUgPSAxLjgsIGFscGhhID0gMC44NSkgKwogICMgdmVydGljYWwgbGluZSBhdCBpbWFnaW5nLXBvc2l0aXZlIGRhdGUKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSAwLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBsaW5ld2lkdGggPSAwLjgsIGNvbG9yID0gImJsYWNrIikgKwogIHNjYWxlX3lfbG9nMTAoYnJlYWtzID0gbG9nX2JyZWFrcywKICAgICAgICAgICAgICAgIGxhYmVscyA9IGxhYmVsX251bWJlcihhY2N1cmFjeSA9IDEpKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcSh4X21pbiwgeF9tYXgsIGJ5ID0gMykpICsgICMgZXZlcnkgMyBtb250aHMKICBsYWJzKAogICAgeCA9ICJNb250aHMgYmVmb3JlL2FmdGVyIGNsaW5pY2FsIHJlY3VycmVuY2UgKDAgPSBpbWFnaW5nIHBvc2l0aXZlKSIsCiAgICB5ID0gImN0RE5BIGxldmVsIChNVE0vbUwpIiwKICAgIGNvbG9yID0gIlBhdGllbnQiCiAgKSArCiAgdGhlbWVfYncoYmFzZV9zaXplID0gMTIpICsKICB0aGVtZSgKICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2xpbmUoY29sb3IgPSAiZ3JleTg1IiwgbGluZXdpZHRoID0gMC4zKSwKICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2xpbmUoY29sb3IgPSAiZ3JleTkyIiwgbGluZXdpZHRoID0gMC4yKSwKICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiAgIyBjaGFuZ2UgdG8gInJpZ2h0IiBpZiB5b3Ugd2FudCB0aGUgbGVnZW5kCiAgKQoKcHJpbnQocCkKYGBgCgoKI1RpbWUgZnJvbSBmaXJzdCBjdEROQSBwb3NpdGl2ZSB0aW1lcG9pbnQgdG8gbGFzdCBpbWFnaW5nIGluIHN1cnZlaWxsYW5jZV9GYWxzZSBQb3NpdGl2ZSBwYXRpZW50cwpgYGB7cn0Kcm0obGlzdD1scygpKQpjc3ZfcGF0aCA8LSAifi9Eb3dubG9hZHMvQ0xJQSBVVFVDX1RpbWUgZnJvbSBjdEROQSB0byBpbWFnaW5nIEZQIHB0cy5jc3YiCmRmIDwtIHJlYWQuY3N2KGNzdl9wYXRoLCBjaGVjay5uYW1lcyA9IEZBTFNFKQppZiAoIkZpbmFsLmNvaG9ydCIgJWluJSBuYW1lcyhkZikpIHsKICBkZiA8LSBkZltkZiRGaW5hbC5jb2hvcnQgPT0gVFJVRSwgXQp9IGVsc2UgewogIHdhcm5pbmcoIkNvbHVtbiAnRmluYWwuY29ob3J0JyBub3QgZm91bmQgaW4gdGhlIGRhdGFzZXQuIikKfQppZF9jYW5kaWRhdGVzIDwtIGMoIlBhdGllbnQiLCJJRCIsIlB0IiwiU3ViamVjdCIsIlNhbXBsZSIpCmlkX2NvbCA8LSBpZF9jYW5kaWRhdGVzW2lkX2NhbmRpZGF0ZXMgJWluJSBuYW1lcyhkZildWzFdCmlmIChpcy5uYShpZF9jb2wpKSB7CiAgZGYgPC0gZGYgJT4lIG11dGF0ZShQYXRpZW50ID0gcm93X251bWJlcigpKQogIGlkX2NvbCA8LSAiUGF0aWVudCIKfQoKbnVtX2NvbHMgPC0gaW50ZXJzZWN0KGMoIk1SIiwiQ1IiLCJMVCIpLCBuYW1lcyhkZikpCmRmW251bV9jb2xzXSA8LSBsYXBwbHkoZGZbbnVtX2NvbHNdLCBmdW5jdGlvbih4KSBzdXBwcmVzc1dhcm5pbmdzKGFzLm51bWVyaWMoeCkpKQoKIyBJZiBMVCBtaXNzaW5nLCBjb21wdXRlIGluIGRheXMKaWYgKCEoIkxUIiAlaW4lIG5hbWVzKGRmKSkpIHsKICBkZiA8LSBkZiAlPiUgbXV0YXRlKExUID0gQ1IgLSBNUikKfQoKIyAtLS0tIGNvbnZlcnQgdG8gbW9udGhzIC0tLS0KIyBBcHByb3hpbWF0ZTogMzAuNDQgZGF5cyBwZXIgbW9udGggKGF2ZXJhZ2UpCmRheXNfdG9fbW9udGhzIDwtIGZ1bmN0aW9uKHgpIHggLyAzMC40MzcKCmRmIDwtIGRmICU+JQogIG11dGF0ZShNUl9tbyA9IGRheXNfdG9fbW9udGhzKE1SKSwKICAgICAgICAgQ1JfbW8gPSBkYXlzX3RvX21vbnRocyhDUiksCiAgICAgICAgIExUX21vID0gZGF5c190b19tb250aHMoTFQpKQoKIyAtLS0tIG9yZGVyaW5nIGZvciB5LWF4aXMgLS0tLQpkZiA8LSBkZiAlPiUKICBtdXRhdGUoRWFybGllc3QgPSBwbWluKE1SX21vLCBDUl9tbywgbmEucm0gPSBUUlVFKSkgJT4lCiAgYXJyYW5nZShFYXJsaWVzdCkgJT4lCiAgbXV0YXRlKFBhdGllbnRfZiA9IGZhY3RvciguZGF0YVtbaWRfY29sXV0sIGxldmVscyA9IC5kYXRhW1tpZF9jb2xdXSkpCgojIC0tLS0gbG9uZyBmb3JtYXQgZm9yIHBvaW50cyAtLS0tCnBvaW50c19sb25nIDwtIGRmICU+JQogIHNlbGVjdChQYXRpZW50X2YsIE1SX21vLCBDUl9tbykgJT4lCiAgcGl2b3RfbG9uZ2VyKGMoTVJfbW8sIENSX21vKSwgbmFtZXNfdG8gPSAiVHlwZSIsIHZhbHVlc190byA9ICJNb250aHMiKSAlPiUKICBtdXRhdGUoVHlwZSA9IGRwbHlyOjpyZWNvZGUoVHlwZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk1SX21vIiA9ICJGaXJzdCBjdEROQSBwb3NpdGl2ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJDUl9tbyIgPSAiTGFzdCBSYWRpb2xvZ2ljYWwgYXNzZXNzbWVudCIpKQoKIyAtLS0tIHNlZ21lbnRzIGJldHdlZW4gTVIgYW5kIENSIC0tLS0Kc2VnbWVudHNfZGYgPC0gZGYgJT4lCiAgdHJhbnNtdXRlKFBhdGllbnRfZiwKICAgICAgICAgICAgeDAgPSBNUl9tbywgeDEgPSBDUl9tbywKICAgICAgICAgICAgbHRfZmxhZyA9IGlmX2Vsc2UoTFQgPiAxMjAsICJndDEyMCIsICJsZTEyMCIpKQoKIyAtLS0tIGFubm90YXRpb24gLS0tLQptZWRfbHQgIDwtIG1lZGlhbihkZiRMVF9tbywgbmEucm0gPSBUUlVFKQptaW5fbHQgIDwtIG1pbihkZiRMVF9tbywgbmEucm0gPSBUUlVFKQptYXhfbHQgIDwtIG1heChkZiRMVF9tbywgbmEucm0gPSBUUlVFKQoKYW5ub3RfbGFiZWwgPC0gc3ByaW50ZigiTWVkaWFuIHRpbWUgZnJvbSBjdEROQSBwb3NpdGl2ZSB0byBsYXN0IGltYWdpbmc6XG4lLjFmIG1vbnRocyAoJS4xZiB0byAlLjFmKSIsCiAgICAgICAgICAgICAgICAgICAgICAgbWVkX2x0LCBtaW5fbHQsIG1heF9sdCkKCiMgLS0tLSBwbG90IC0tLS0KcGFsIDwtIGMoIkZpcnN0IGN0RE5BIHBvc2l0aXZlIiA9ICJibGFjayIsCiAgICAgICAgICJMYXN0IFJhZGlvbG9naWNhbCBhc3Nlc3NtZW50IiAgPSAicmVkIikKCnhfbWF4IDwtIG1heChjKGRmJE1SX21vLCBkZiRDUl9tbyksIG5hLnJtID0gVFJVRSkKeV9taWQgPC0gbGV2ZWxzKGRmJFBhdGllbnRfZilbY2VpbGluZyhubGV2ZWxzKGRmJFBhdGllbnRfZikgKiAwLjU1KV0KCnAgPC0gZ2dwbG90KCkgKwogIGdlb21fc2VnbWVudChkYXRhID0gc2VnbWVudHNfZGYsCiAgICAgICAgICAgICAgIGFlcyh4ID0geDAsIHhlbmQgPSB4MSwgeSA9IFBhdGllbnRfZiwgeWVuZCA9IFBhdGllbnRfZiwKICAgICAgICAgICAgICAgICAgIGxpbmV0eXBlID0gbHRfZmxhZyksCiAgICAgICAgICAgICAgIGxpbmV3aWR0aCA9IDAuNiwgY29sb3IgPSAiYmxhY2siKSArCiAgc2NhbGVfbGluZXR5cGVfbWFudWFsKHZhbHVlcyA9IGMobGUxMjAgPSAic29saWQiLCBndDEyMCA9ICJkYXNoZWQiKSwKICAgICAgICAgICAgICAgICAgICAgICAgZ3VpZGUgPSAibm9uZSIpICsKICBnZW9tX3BvaW50KGRhdGEgPSBwb2ludHNfbG9uZywKICAgICAgICAgICAgIGFlcyh4ID0gTW9udGhzLCB5ID0gUGF0aWVudF9mLCBjb2xvciA9IFR5cGUpLAogICAgICAgICAgICAgc2l6ZSA9IDIuOCkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBwYWwsIG5hbWUgPSBOVUxMKSArCiAgbGFicyh4ID0gIk1vbnRocyBmcm9tIGN0RE5BIHBvc2l0aXZpdHkiLCB5ID0gTlVMTCkgKwogIGFubm90YXRlKCJ0ZXh0IiwKICAgICAgICAgICB4ID0geF9tYXggKiAwLjczLAogICAgICAgICAgIHkgPSB5X21pZCwKICAgICAgICAgICBsYWJlbCA9IGFubm90X2xhYmVsLAogICAgICAgICAgIGhqdXN0ID0gMCwgdmp1c3QgPSAwLjUsIHNpemUgPSAzLjgpICsKICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsIGNlaWxpbmcoeF9tYXgvMykqMywgYnkgPSAzKSkgKwogIGNvb3JkX2NhcnRlc2lhbih4bGltID0gYygwLCB4X21heCAqIDEuMDUpKSArCiAgdGhlbWVfYncoYmFzZV9zaXplID0gMTIpICsKICB0aGVtZSgKICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2xpbmUoY29sb3IgPSAiZ3JleTg1IiwgbGluZXdpZHRoID0gMC4zKSwKICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2xpbmUoY29sb3IgPSAiZ3JleTkyIiwgbGluZXdpZHRoID0gMC4yKSwKICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSA5KQogICkKCnByaW50KHApCgp3Y19kZiA8LSBkZiAlPiUgZHBseXI6OnNlbGVjdChNUiwgQ1IpICU+JSB0aWR5cjo6ZHJvcF9uYSgpCnBhaXJlZF9kaWZmIDwtIHdjX2RmJENSIC0gd2NfZGYkTVIKCndpbHggPC0gd2lsY294LnRlc3QoCiAgeCA9IHdjX2RmJENSLCB5ID0gd2NfZGYkTVIsCiAgcGFpcmVkID0gVFJVRSwKICBhbHRlcm5hdGl2ZSA9ICJ0d28uc2lkZWQiLAogIGNvbmYuaW50ID0gVFJVRSwgICAgICAjIGdpdmVzIENJIGZvciB0aGUgSG9kZ2Vz4oCTTGVobWFubiBlc3RpbWF0ZSBvZiB0aGUgbWVkaWFuIGRpZmZlcmVuY2UKICBleGFjdCA9IEZBTFNFICAgICAgICAgIyBzYWZlciBmb3IgdGllcy9sYXJnZSBOCikKCldfc3RhdCAgIDwtIHVubmFtZSh3aWx4JHN0YXRpc3RpYykgICAgICAgICAgIyBXaWxjb3hvbiBWCnBfdmFsdWUgIDwtIHdpbHgkcC52YWx1ZQpITF9lc3QgICA8LSB1bm5hbWUod2lseCRlc3RpbWF0ZSkgICAgICAgICAgICMgbWVkaWFuIG9mIChDUiAtIE1SKQpITF9jaSAgICA8LSB3aWx4JGNvbmYuaW50ICAgICAgICAgICAgICAgICAgICMgQ0kgZm9yIG1lZGlhbiBkaWZmZXJlbmNlCgojIFNpbXBsZSBwLXZhbHVlIGZvcm1hdHRlciBmb3IgYW5ub3RhdGlvbi9wdWJsaWNhdGlvbgpmbXRfcCA8LSBmdW5jdGlvbihwKSB7CiAgaWYgKGlzLm5hKHApKSByZXR1cm4oIk5BIikKICBpZiAocCA8IDAuMDAxKSAiPCAwLjAwMSIgZWxzZSBzcHJpbnRmKCI9ICUuM2YiLCByb3VuZChwLCAzKSkKfQpwX3RleHQgPC0gcGFzdGUwKCJQICIsIGZtdF9wKHBfdmFsdWUpKSAgIyBlLmcuLCAiUCA9IDAuMDAzIiBvciAiUCA8IDAuMDAxIgoKIyBPcHRpb25hbDogcHJpbnQgYSBjb21wYWN0IHN1bW1hcnkKY2F0KCJcblBhaXJlZCBXaWxjb3hvbiBzaWduZWQtcmFuayB0ZXN0IChDUiB2cyBNUilcbiIsCiAgICAiLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuIiwKICAgIHNwcmludGYoIk4gcGFpcnM6ICVkIiwgbnJvdyh3Y19kZikpLCAiXG4iLAogICAgc3ByaW50ZigiVyAoVik6ICVnIiwgV19zdGF0KSwgIlxuIiwKICAgIHNwcmludGYoIlAtdmFsdWU6ICVzIiwgaWZlbHNlKHBfdmFsdWUgPCAwLjAwMSwgIjwgMC4wMDEiLCBzcHJpbnRmKCIlLjZmIiwgcF92YWx1ZSkpKSwgIlxuIiwKICAgIHNwcmludGYoIkhvZGdlc+KAk0xlaG1hbm4gbWVkaWFuIGRpZmZlcmVuY2UgKENSIC0gTVIpOiAlLjFmIGRheXMiLCBITF9lc3QpLCAiXG4iLAogICAgc3ByaW50ZigiOTUlJSBDSTogWyUuMWYsICUuMWZdIGRheXMiLCBITF9jaVsxXSwgSExfY2lbMl0pLCAiXG4iLCBzZXAgPSAiIikKYGBgCgoKI0luZGl2aWR1YWwgUHQgUGxvdHNfRmFsc2UgUG9zaXRpdmUgcGF0aWVudHMKYGBge3J9CiNGaWx0ZXIgZm9yIFBhdGllbnQgVVRVQy0wMTAKcm0obGlzdD1scygpKQpzZXR3ZCgifi9Eb3dubG9hZHMiKQpjaXJjX2RhdGEgPC0gcmVhZC5jc3YoIlJXRSBVVFVDX1B0IFNwZWNpZmljIFBsb3QgZGF0YS5jc3YiKQpwbG90X2RhdGEgPC0gY2lyY19kYXRhICU+JSAKICBmaWx0ZXIoUGF0aWVudE5hbWUgPT0gIlVUVUMtMDEwIikgCgojIDIuIFByb2Nlc3MgbGF5ZXJzCiMgSGFuZGxlIGN0RE5BIGxvZy1zY2FsZSBwc2V1ZG8temVybwpkYXRhX1NpZ25hdGVyYSA8LSBwbG90X2RhdGEgJT4lIAogIGZpbHRlcihFdmVudF90eXBlICVpbiUgYygiY3RETkFfcG9zIiwgImN0RE5BX25lZyIpKSAlPiUKICBtdXRhdGUoTVRNX0xvZyA9IGlmZWxzZShNVE0ubUwgPT0gMCwgMC4wMDEsIE1UTS5tTCkpCgpkYXRhX0ltYWdpbmcgPC0gcGxvdF9kYXRhICU+JSAKICBmaWx0ZXIoRXZlbnRfdHlwZSA9PSAiSW1hZ2luZyIpCgojIFRyZWF0bWVudCBibG9ja3MKZGF0YV9UcmVhdG1lbnQgPC0gcGxvdF9kYXRhICU+JSAKICBmaWx0ZXIoIWlzLm5hKFR4X3R5cGUpICYgVHhfdHlwZSAhPSAiTkEiICYgIWlzLm5hKFR4X3N0YXJ0Lm1vbnRocykpICU+JQogIGRpc3RpbmN0KFR4X3R5cGUsIFR4X3N0YXJ0Lm1vbnRocywgVHhfZW5kLm1vbnRocykKCiMgMy4gQ3JlYXRlIHRoZSBQbG90CmdncGxvdCgpICsKICAjIFRyZWF0bWVudCBSZWN0YW5nbGVzCiAgZ2VvbV9yZWN0KGRhdGEgPSBkYXRhX1RyZWF0bWVudCwgCiAgICAgICAgICAgIGFlcyh4bWluID0gVHhfc3RhcnQubW9udGhzLCB4bWF4ID0gVHhfZW5kLm1vbnRocywgeW1pbiA9IDAuMDA0LCB5bWF4ID0gMTApLCAKICAgICAgICAgICAgZmlsbCA9ICJsaWdodGJsdWUiLCBhbHBoYSA9IDAuMTUpICsKICBnZW9tX3RleHQoZGF0YSA9IGRhdGFfVHJlYXRtZW50LCAKICAgICAgICAgICAgYWVzKHggPSAoVHhfc3RhcnQubW9udGhzICsgVHhfZW5kLm1vbnRocykvMiwgeSA9IDUsIGxhYmVsID0gVHhfdHlwZSksIAogICAgICAgICAgICBhbmdsZSA9IDkwLCBjb2xvciA9ICJibGFjayIsIHNpemUgPSAzLCBmb250ZmFjZSA9ICJib2xkIikgKwogIAogICMgY3RETkEgTGluZQogIGdlb21fbGluZShkYXRhID0gZGF0YV9TaWduYXRlcmEsIAogICAgICAgICAgICBhZXMoeCA9IGRhdGUuZGlmZi5tb250aHMsIHkgPSBNVE1fTG9nKSwgY29sb3IgPSAiYmxhY2siLCBsaW5ld2lkdGggPSAwLjcpICsKICAKICAjIGN0RE5BIFBvaW50cyAoTWFwcGVkIHRvIEZpbGwpCiAgZ2VvbV9wb2ludChkYXRhID0gZGF0YV9TaWduYXRlcmEsIAogICAgICAgICAgICAgYWVzKHggPSBkYXRlLmRpZmYubW9udGhzLCB5ID0gTVRNX0xvZywgZmlsbCA9IEV2ZW50X3R5cGUpLCAKICAgICAgICAgICAgIHNoYXBlID0gMjEsIHNpemUgPSAzLjUsIGNvbG9yID0gImJsYWNrIikgKwogIAogICMgSW1hZ2luZyBQb2ludHMgKE1hcHBlZCB0byBTaGFwZSBhbmQgQ29sb3IgdG8gY3JlYXRlIGEgc2Vjb25kIGxlZ2VuZCkKICBnZW9tX3BvaW50KGRhdGEgPSBkYXRhX0ltYWdpbmcsIAogICAgICAgICAgICAgYWVzKHggPSBkYXRlLmRpZmYubW9udGhzLCB5ID0gMC4wMDUsIGNvbG9yID0gIk5FRCBpbiBTY2FuIiksIAogICAgICAgICAgICAgc2hhcGUgPSAyNSwgc2l6ZSA9IDQsIGZpbGwgPSAiZGFya2dyZWVuIikgKwogIAogICMgWC1BeGlzOiAzLW1vbnRoIGludGVydmFscwogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoMCwgbWF4KHBsb3RfZGF0YSRkYXRlLmRpZmYubW9udGhzLCBuYS5ybT1UKSArIDMsIGJ5ID0gMykpICsKICAKICAjIFktQXhpczogTG9nMTAgc2NhbGUKICBzY2FsZV95X2xvZzEwKGJyZWFrcyA9IGMoMC4wMDEsIDAuMDEsIDAuMSwgMSwgMTApLAogICAgICAgICAgICAgICAgbGFiZWxzID0gYygiMCIsICIwLjAxIiwgIjAuMSIsICIxIiwgIjEwIiksCiAgICAgICAgICAgICAgICBsaW1pdHMgPSBjKDAuMDAxLCAxNSkpICsKICAKICAjIExlZ2VuZCBmb3IgY3RETkEKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJjdEROQV9wb3MiID0gImJsYWNrIiwgImN0RE5BX25lZyIgPSAid2hpdGUiKSwgCiAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gYygiTmVnYXRpdmUiLCAiUG9zaXRpdmUiKSwgCiAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICJjdEROQSBTdGF0dXMiKSArCiAgCiAgIyBMZWdlbmQgZm9yIEltYWdpbmcKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiTkVEIGluIFNjYW4iID0gImRhcmtncmVlbiIpLCAKICAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICJJbWFnaW5nIFJlc3VsdCIpICsKICAKICAjIFN0eWxpbmcgdGhlIGxlZ2VuZCB0byBzaG93IHRoZSB0cmlhbmdsZSBjb3JyZWN0bHkKICBndWlkZXMoY29sb3IgPSBndWlkZV9sZWdlbmQob3ZlcnJpZGUuYWVzID0gbGlzdChzaGFwZSA9IDI1LCBmaWxsID0gImRhcmtncmVlbiIpKSkgKwogIAogIGxhYnMoeCA9ICJNb250aHMgZnJvbSBTdXJnZXJ5IiwgCiAgICAgICB5ID0gIk1UTS9tTCIsIAogICAgICAgdGl0bGUgPSAiQ2xpbmljYWwgVGltZWxpbmU6IFVUVUMtMDEwIikgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUocGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiKQoKI0ZpbHRlciBmb3IgUGF0aWVudCBVVFVDLTAxMQpybShsaXN0PWxzKCkpCnNldHdkKCJ+L0Rvd25sb2FkcyIpCmNpcmNfZGF0YSA8LSByZWFkLmNzdigiUldFIFVUVUNfUHQgU3BlY2lmaWMgUGxvdCBkYXRhLmNzdiIpCnBsb3RfZGF0YSA8LSBjaXJjX2RhdGEgJT4lIAogIGZpbHRlcihQYXRpZW50TmFtZSA9PSAiVVRVQy0wMTEiKSAKCiMgMi4gUHJvY2VzcyBsYXllcnMKIyBIYW5kbGUgY3RETkEgbG9nLXNjYWxlIHBzZXVkby16ZXJvCmRhdGFfU2lnbmF0ZXJhIDwtIHBsb3RfZGF0YSAlPiUgCiAgZmlsdGVyKEV2ZW50X3R5cGUgJWluJSBjKCJjdEROQV9wb3MiLCAiY3RETkFfbmVnIikpICU+JQogIG11dGF0ZShNVE1fTG9nID0gaWZlbHNlKE1UTS5tTCA9PSAwLCAwLjAwMSwgTVRNLm1MKSkKCmRhdGFfSW1hZ2luZyA8LSBwbG90X2RhdGEgJT4lIAogIGZpbHRlcihFdmVudF90eXBlID09ICJJbWFnaW5nIikKCiMgVHJlYXRtZW50IGJsb2NrcwpkYXRhX1RyZWF0bWVudCA8LSBwbG90X2RhdGEgJT4lIAogIGZpbHRlcighaXMubmEoVHhfdHlwZSkgJiBUeF90eXBlICE9ICJOQSIgJiAhaXMubmEoVHhfc3RhcnQubW9udGhzKSkgJT4lCiAgZGlzdGluY3QoVHhfdHlwZSwgVHhfc3RhcnQubW9udGhzLCBUeF9lbmQubW9udGhzKQoKIyAzLiBDcmVhdGUgdGhlIFBsb3QKZ2dwbG90KCkgKwogICMgVHJlYXRtZW50IFJlY3RhbmdsZXMKICBnZW9tX3JlY3QoZGF0YSA9IGRhdGFfVHJlYXRtZW50LCAKICAgICAgICAgICAgYWVzKHhtaW4gPSBUeF9zdGFydC5tb250aHMsIHhtYXggPSBUeF9lbmQubW9udGhzLCB5bWluID0gMC4wMDQsIHltYXggPSAxMCksIAogICAgICAgICAgICBmaWxsID0gImxpZ2h0Ymx1ZSIsIGFscGhhID0gMC4xNSkgKwogIGdlb21fdGV4dChkYXRhID0gZGF0YV9UcmVhdG1lbnQsIAogICAgICAgICAgICBhZXMoeCA9IChUeF9zdGFydC5tb250aHMgKyBUeF9lbmQubW9udGhzKS8yLCB5ID0gNSwgbGFiZWwgPSBUeF90eXBlKSwgCiAgICAgICAgICAgIGFuZ2xlID0gOTAsIGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IDMsIGZvbnRmYWNlID0gImJvbGQiKSArCiAgCiAgIyBjdEROQSBMaW5lCiAgZ2VvbV9saW5lKGRhdGEgPSBkYXRhX1NpZ25hdGVyYSwgCiAgICAgICAgICAgIGFlcyh4ID0gZGF0ZS5kaWZmLm1vbnRocywgeSA9IE1UTV9Mb2cpLCBjb2xvciA9ICJibGFjayIsIGxpbmV3aWR0aCA9IDAuNykgKwogIAogICMgY3RETkEgUG9pbnRzIChNYXBwZWQgdG8gRmlsbCkKICBnZW9tX3BvaW50KGRhdGEgPSBkYXRhX1NpZ25hdGVyYSwgCiAgICAgICAgICAgICBhZXMoeCA9IGRhdGUuZGlmZi5tb250aHMsIHkgPSBNVE1fTG9nLCBmaWxsID0gRXZlbnRfdHlwZSksIAogICAgICAgICAgICAgc2hhcGUgPSAyMSwgc2l6ZSA9IDMuNSwgY29sb3IgPSAiYmxhY2siKSArCiAgCiAgIyBJbWFnaW5nIFBvaW50cyAoTWFwcGVkIHRvIFNoYXBlIGFuZCBDb2xvciB0byBjcmVhdGUgYSBzZWNvbmQgbGVnZW5kKQogIGdlb21fcG9pbnQoZGF0YSA9IGRhdGFfSW1hZ2luZywgCiAgICAgICAgICAgICBhZXMoeCA9IGRhdGUuZGlmZi5tb250aHMsIHkgPSAwLjAwNSwgY29sb3IgPSAiTkVEIGluIFNjYW4iKSwgCiAgICAgICAgICAgICBzaGFwZSA9IDI1LCBzaXplID0gNCwgZmlsbCA9ICJkYXJrZ3JlZW4iKSArCiAgCiAgIyBYLUF4aXM6IDMtbW9udGggaW50ZXJ2YWxzCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLCBtYXgocGxvdF9kYXRhJGRhdGUuZGlmZi5tb250aHMsIG5hLnJtPVQpICsgMywgYnkgPSAzKSkgKwogIAogICMgWS1BeGlzOiBMb2cxMCBzY2FsZQogIHNjYWxlX3lfbG9nMTAoYnJlYWtzID0gYygwLjAwMSwgMC4wMSwgMC4xLCAxLCAxMCksCiAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCIwIiwgIjAuMDEiLCAiMC4xIiwgIjEiLCAiMTAiKSwKICAgICAgICAgICAgICAgIGxpbWl0cyA9IGMoMC4wMDEsIDE1KSkgKwogIAogICMgTGVnZW5kIGZvciBjdEROQQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoImN0RE5BX3BvcyIgPSAiYmxhY2siLCAiY3RETkFfbmVnIiA9ICJ3aGl0ZSIpLCAKICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJOZWdhdGl2ZSIsICJQb3NpdGl2ZSIpLCAKICAgICAgICAgICAgICAgICAgICBuYW1lID0gImN0RE5BIFN0YXR1cyIpICsKICAKICAjIExlZ2VuZCBmb3IgSW1hZ2luZwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJORUQgaW4gU2NhbiIgPSAiZGFya2dyZWVuIiksIAogICAgICAgICAgICAgICAgICAgICBuYW1lID0gIkltYWdpbmcgUmVzdWx0IikgKwogIAogICMgU3R5bGluZyB0aGUgbGVnZW5kIHRvIHNob3cgdGhlIHRyaWFuZ2xlIGNvcnJlY3RseQogIGd1aWRlcyhjb2xvciA9IGd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXMgPSBsaXN0KHNoYXBlID0gMjUsIGZpbGwgPSAiZGFya2dyZWVuIikpKSArCiAgCiAgbGFicyh4ID0gIk1vbnRocyBmcm9tIFN1cmdlcnkiLCAKICAgICAgIHkgPSAiTVRNL21MIiwgCiAgICAgICB0aXRsZSA9ICJDbGluaWNhbCBUaW1lbGluZTogVVRVQy0wMTEiKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZShwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIpCmBgYAoK